If I was really exited to hear about Android Architecture Components in Google I/O 2017. In particular Room Persistence Library. I had pleasure talking with folks working on it in person during I/O! I want to try it for my own.
I had been using Kotlin prior to the I/O announcement. Having google fully commit it to as a first class citizen on Android was very encouraging.
In this post, I wanted to show how you can start using Room with Kotlin.
I started with a shell project with Dagger 2 setup. We will implement Room in Kotlin project using Dagger2, later will also integrate it with RxJava2.
I am going to use a simple “ToDoList” app, that allows users to add a Task in the application. So first off, lets include the library.
In your build.gradle file, include the room dependency. (1.0.0-alpha1 was the latest version at the time of this writing)
Let’s now create `Task` entity. For now we will make it simple with id, description and boolean flag to indicate if the task is completed.
If you notice, it is for the most part just a regular “data class“. We are just adding annotation for Room to make sense of it.
@Entity(tableName = “task”) as it denotes, is using the table name called “task”. If name is not specified, by default class name is used as the Table name.
@ColumnInfo annotation on a field relating it to the column on the table. e.g. in this example you can see that on the db column name uses “_”.
@PrimaryKey(autoGenerate = true) is applied to “id” field. which in this case is autogenerated. An entity must have at least 1 PrimaryKey.
Dao is where Room does its magic. We just need to define a interface along with the SQL queries. Room at compile time generates the actual implementation of this class for us to use. This may remind you of Retrofit, This is exactly what happening here.
As you can see, we have an interface which is annotated with @Dao, in there we can see multiple functions annotated with various other annotations.
Lets look at one of them, “fun getAllTasks(): List”, this function returns list of all tasks from the database. This function is annotated with @Query annotation. In there we have the sql query specified. This query is validated at compile time. If the query is malformed it will fail the build. With this you can feel confident that if it compiles, it will work.
Now let’s look at the little more complex one. “fun findTaskById(id: Long): Task”. This function is annotated with @Query(“select * from task where id = :p0”).
There is a currently a bug where kotlin converts the parameters to p0, arg0 etc. Hence, the query above specified “:p0”. Ideally we should be able to say “:id”. This will be fixed in the near future. Until then, pay close attention to compile errors to identify this type of mismatch.
We define our database by creating an abstract class that extends RoomDatabase.
Class is annotated with @Database which defines the all the entities(table) it contains, its version. If you look closely, “exportSchema” is set to false here. If you do not, it defaults to “true” which generates a compile time warning as you can see blow:
warning: Schema export directory is not provided to the annotation processor so we cannot export the schema. You can either provide `room.schemaLocation` annotation processor argument OR set exportSchema to false.
This is class pretty much like dagger component, this exposes Dao we defined above. Here we are exposing “TaskDao”
Configuring in Dagger
Like dagger, we will build the room database. Note, this is an expensive operation so, we would want a singleton object. Lets look at the configuration here:
We are building Room database using the application context, We point to the abstract class we defined above, database file name we want.
We are calling following function so that we can run queries in main thread.
If we did not call this, we would see an exception indicating that we cannot access database on main thread.
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long periods of time.
In the later part we will be using RxJava and we will get rid of this. For now, lets move on. We are using Dagger to provide the TaskDao also.
Getting Entities in Presenter
In this example we are using MVP pattern. Lets see how we can get the entities and show it in a recycler view.
Thats it! At this point we are able to define Task entity, use Room to fetch the entities and display them on a Recycler view.
Using Room with RxJava/RxAndroid with Kotlin
Let’s take this up a notch by introducing RxJava/RxAndroid. Add the RxJava/RxAndroid dependency in our build file by adding the following.
compile "io.reactivex.rxjava2:rxjava:2.1.0" compile "io.reactivex.rxjava2:rxandroid:2.0.1"
We would also need to add one more dependency
With this we can now start using Room with RxJava.
Let’s change our function that returned list of Task to return a Flowable.
@Query("select * from task") fun getAllTasks(): Flowable<List<Task>>
With this we can now remove “allowMainThreadQueries()”. Our Module would simply do
<pre>@Provides fun providesAppDatabase(context: Context): AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, "my-todo-db").build()
We will then need to modify our presenter to use the updated function. Here is the full presenter.
And that is it!, At this point you have Room, Dagger, RxJava all working together using Kotlin!
You can find completed sample at https://github.com/manijshrestha/ToDoList