Hello, guys, this is the last part of the Gentle series, in which we have been looking at the Android Architecture Components framework. In case you have not, check out my articles about LiveData and ViewModel. In this last part we will talk about Room, what it is and why you should be using it to replace that old SQLiteOpenHelper.
Android has supported SQLite out of the box since the beginning, but it has not been always as pleasant to work with, specially on projects of increasing complexity. Some of the most troublesome issues when working with SQLite on Android:
- Lack of compile time SQL queries verification; one name of a table or column written wrong and you will end up with a runtime crash.
- Writing boilerplate code to map your model objects to queries and vice versa.
- Manually having to modify your queries as your schema is changed.
Room provides an abstraction layer on top of SQLite database, it resolves the issues mentioned above and allows you to write more expressive code to define your schema, write and perform queries and upgrade your database as your project evolves.
Before using Room in your project, you first need to add the required dependencies to your project:
- Open the project level build.gradle file and add the
google()repository as shown below:
2. Now open the module level build.gradle file and add the dependencies as shown below:
3. That’s it. Now you only need to Sync your gradle files.
In order to implement Room in your project, you will need to create some classes:
- Entity: It represents a Table in the generated Database.
An Entity is an annotated class that represents a Database Table. Room will generate a table from the class definition, in this case the resulting User table will contain three columns (named ‘user_id’, ‘first_name’ and ‘last_name) and a primary key, set to ‘user_id’ column. You can customize the resulting schema using the @Entity and @ColumnInfo annotation parameters.
2. DAO: Stands for Data Access Object. It handles the mapping between queries and functions, you call the functions and Room handles the rest.
A DAO is an interface or abstract class annotated with @Dao, it contains the methods that Room will map to queries in order to interact with your tables, if it’s an abstract class instead of an interface it may contain a constructor with a RoomDatabase as it’s only argument. Even when you can have all the queries for all your tables in a single DAO, is highly recommended to have multiple DAOs depending on the tables they actually touch in order to keep them small and concise. The methods themselves are annotated with@Insert, @Delete or @Query annotations. Any query that you write for the@Query annotation is validated at compile time, so if you misspell the name of a table or column Android Studio will let you know, so we can avoid unwanted runtime crashes.
1. The query that returns a LiveData object will be executed asynchronously in contrast with one that does not.
2. Room will automatically perform the wrapping for you, so the observers of the LiveData get notified once the result set is ready.
3. Room Database: It’s the layer on top of the actual SQLite Database, uses the *DAO* objects to take care of the tasks you used to handle using the SQLiteOpenHelper.
A database is a an abstract class that extends from RoomDatabase and it’s annotated with @Database. It’s this object that you will interact with in order to get the DAOs to perform the actual queries; you need to indicate Room which entities are going to be included in this particular database through the entities argument in the annotation, also you will need to indicate the version of the database, this is needed so Room can appropriately keep track of the schema version and run the migrations (which we will talk about later) if needed.
Once you have got the instance of the database you can perform the queries using the DAOs you defined earlier:
Room doesn’t support database access on the main thread unless you’ve called allowMainThreadQueries()on the builder because it might lock the UI for a long period of time. Queries that return instances of LiveData or Flowable are exempt from this.
As your project evolves you will probably want to add more features to your app and maybe these are reflected in changes on your database entities, having your users to lose all of their stored data could lead to a poor user experience, not to mention the cases where you can’t retrieve it again from a remote server.
That’s why Room allows you to write Migration classes that let you modify the schema of your database, at runtime it will run each of the migrations in order depending on the version the database is being upgraded from.
So, in this example, if the user is updating your app from one that has a database version 1 to version 2, Room will run the migration to add the new field in the User table.
Where do the other components fit in?
You can use Room as a standalone component, but usually you would want to take advantage of the synergy that all of the Architecture Components have with each other, so the recommended architecture would be one similar to:
The access to the actual database is performed through the Repository which is just an ordinary class that serves as an abstraction between the ViewModel and the data source; its main purpose is to hide where the data comes from, you could show cached data in your database while fetching the most up-to-date data from the network and serve it to the ViewModel so the UI Controller can show it to the user, all of this transparent to the higher level classes.
As with the other components, Room looks to solve some specific problems that Android developers have been dealing with for quite a long time and IMHO it does it very well, it gives you features that you can take advantage of right away, no matter how big your project is and it’s worth to keep in your Android arsenal.