Room 🔗 Coroutines

Add some suspense to your database

Room 2.1 adds support for Kotlin coroutines. DAO methods can now be marked as suspending to ensure that they are not executed on the main thread. Read on to see how to use this, how it works under the hood and how to test this new functionality.

Add some suspense to your database

To use coroutines and Room in your app, update to Room 2.1 and add the new dependency to your build.gradle file:

implementation "androidx.room:room-coroutines:${versions.room}"

You’ll also need Kotlin 1.3.0 and Coroutines 1.0.0 or newer.

You can now update your DAO methods to use suspension functions:

DAO with suspend methods

@Transaction methods can also be suspending and they can call other suspending DAO functions:

DAO with suspend transaction function

You can also call suspension functions from different DAOs inside a transaction:

Calling different DAO suspending functions in a transaction

You can provide executors (by calling setTransactionExecutor or setQueryExecutor when you’re building your database) to control the threads they run on. By default this will be the same executor used for running queries on the background thread.

Testing DAO suspension functions

Testing a DAO suspending function is no different from testing any other suspending function. For example, to check that after inserting a user we are able to retrieve it, we wrap the test in a runBlocking block:

Testing DAO suspend functions

Under the hood

To see what’s under the hood, let’s take a look at the DAO class implementation Room generates for a synchronous and for a suspending insert:

Synchronous and suspending insert functions

For the synchronous insert, the generated code starts a transaction, executes the insert, marks the transaction as successful and ends it. The synchronous method will just execute the insert on whatever thread it’s called from.

Room synchronous insert generated implementation

Now let’s see how adding the suspend modifier changes things:

Room suspending insert generated implementation

The generated code ensures that the insert happens off of the UI thread. In our suspend function implementation, the same logic from the synchronous insert method is wrapped in a `Callable`. Room calls the `CoroutinesRoom.execute` suspend function, which switches to a background dispatcher, depending on whether the database is opened and we are in a transaction or not. Here’s the implementation of the function:

CoroutinesRoom.execute implementation

Case 1. The database is opened and we are in a transaction

Here we just immediately execute the callable — i.e. the actual insertion of the user in the database

Case 2. Otherwise

Room makes sure that the work done in the Callable#call method is performed on a background thread.

Room will use different Dispatchers for transactions and queries. These are derived from the executors you provide when building your Database or by default will use the Architecture Components IO executor. This is the same executor that would be used by LiveData to do background work.

If you’re interested to check out the implementation, check out the CoroutinesRoom.java and RoomDatabase.kt