Our team is committed to providing the best native app experience and this mean providing a good offline experience when the app losses connectivity. The only way to achieve this is to use a database or to add smart caching to your app.
Recently we faced a situation where we needed to change the schema for several existing database tables in our app. About 12 months ago we migrated from SQLiteOpenHelper to Requery which really simplified our code. I was quite impressed with the new Android Architeture Components that came out at Google IO last year and decided to take a deep dive into understanding Room. As we had already started using several architecture components we could see several advantages to using Room so we decided to bite the bullet and convert our simple standalone database tables first.
Our journey with Room first started with trying to find out how our existing table schemas were defined. Many of these tables were created long before I started working on the app. I had a basic idea of what the tables looked like from the Requery entities but had no idea what indexes or field constraints where defined on these tables. Joe one of the Android developers I work with showed me Android Studio’s Device File Explorer which makes it really easy to access any file on your device. I used this tool to export the database from the database directory. In our case the directory path is data/data/au.com.auspost.android/debug/database
The next step was to find out what was inside the database using a command line tool called sqlite3. This command-line tool is located under your sdk directory Android/sdk/platform-tools.
Load the database schema.
> sqlite auspost_db
List the tables.
android_metadata branch_location parcel
Describe the table branch_location (this table contains all the postcodes in Australia)
sqlite> .schema branch_location
CREATE TABLE "branch_location"(id INTEGER,
The next step involved changing our code to use Room so we decided to start with mapping our entities. Here is one of the entities that we mapped.
About 4 months ago we also started using Kotlin and and team loves the more concise syntax. We used Kotlin’s data class to define our entities and this really slimmed down the code compared to Java. (On our project we also use Parcler so we needed to add the extra annotation and constructor to our classes)
Next up we created the DAO object to query our database table. The really nice feature of using Room in Android Studio is that it checks your SQL queries at compile time so its hard to mess up your queries if you miss spell your column names or SQL syntax. I also prefer the queries to be defined as a string rather than using methods like and() and or() in Requery as this means you can drop your raw queries into your favourite database tools to test your queries.
To use the DAO object, we first need to create a database class that defines the entities and the database version.
Next up we create an intermediary class that is used by our fragments and activities. Our manager is pretty straight forward we decided not map our entities to domain objects as the objects would be nearly identical.
Finally, we create a migration class.
Our project uses Toothpick for dependency injection so we use the provider class to create a single instance of the database class. Note that we also use the fallbackToDestructiveMigration() when building the database so if the migration fails we still end up with an empty working database.
We found that each Room entity requires a primary key which meant that we needed to update the table schema to include this. As Sqlite is restricted in what it can modify on an existing table we created a new table schema, copied the data, dropped the old table schema and then renamed the new table. A nice feature in Kotlin is the triple quotes which enables you to write human readable SQL statements in code. (Another one of Joes tips)
Now on to testing.
One of the other primary reasons that we moved to Room was its testability. The Room framework also provides an in-memory database builder to test your DAO classes as well as your database migrations.
We found Room really easy to integrate into our app. The compile time syntax checking, testability, clean APIs and familiar DAO pattern (if you have a Spring Java background) make it a breeze to use. We converted 3 standalone tables to use Room however we decided to leave our more complicated nested tables with foreign keys in Requery. We see Room as great replacement for the low level SQL APIs however it might not be suitable in all cases.
This isn’t the end of our journey with Room as we will continue to use on future projects were appropriate.
If you enjoyed reading this article take a look at my follow on article — Paging Adapter — new atchitecture component
Update 24/12/2018: Recently we converted our Requery code to Kotlin and we were also contemplating about migrating our remaining Requery code to Room. After detailed analysis, we believe the current version of Room does not have the functionality to support complex relationships. Although this may change in future versions of Room, if your database requires complex relationships then Requery, Realm or other databases maybe a better option.