△ Support for Flow in Room

Jetpack Room’s support for coroutines is growing: Room 2.1 added support for coroutines and one-shot reads and writes. Room 2.2 added observability for reads and writes through Flow, which allows you to be notified when data changes in the database.

△ Room supports asynchronous Query operations

The Flow field

Suppose we have a database of dog information, and its name field is the primary key. Therefore, it is impossible to have the same data in two name fields in the database at the same time, that is, each dog is unique.

@Entity
data class Dog (
    @PrimaryKey val name: String,
    val cuteness: Int.val barkingVolume: Int
)
Copy the code

To get a total table of all the puppies from the data, we write the following query statement in the DAO:

@Query("SELECT * FROM Dog")
fun getAllDogs(a): List<Dog>
Copy the code

Because the bark of the dog, the barkingVolume field, changes over time, and we want to make sure the UI is up to date. So we want to be able to notify us when data in the database changes, such as additions, deletions, or updates.

To do this, we return a Flow object by updating the Query operation.

@Query("SELECT * FROM Dog")
fun getAllDogs(a): Flow<List<Dog>>
Copy the code

As such, the master table with puppy information is redistributed every time the data in the database is updated. For example, suppose we have the following data in our database:

(Frida, 11, 3)
(Bandit, 12, 5)
Copy the code

The first time getAllDogs is called, the Flow dispatches the following data:

[(Frida, 11, 3), (Bandit, 12, 5)]
Copy the code

If Bandit is excited, his barkingVolume will be changed to 6: (Bandit, 12,6), and Flow will redistribute the latest data, so the whole list will be updated to:

[(Frida, 11, 3), (Bandit, 12, 6)]
Copy the code

Now let’s look at the operation to get the details of a single puppy. To get the latest puppy data in real time, we return Flow:

@Query("SELECT * FROM Dog WHERE name = :name")
fun getDog(name: String): Flow<Dog>
Copy the code

If we call getDog(“Frida”), the Flow returns an object: (Frida, 11, 3).

Whenever any data in the database is updated, no matter which row of data is changed, the query operation is re-executed and the Flow is distributed again, so that we receive the latest data when Frida is updated. Similarly, if an unrelated piece of data, such as a puppy Bandit update, is sent to our Flow, it will receive the same data as before: (Frida, 11, 3).

This is because the SQLite database’s content update notification function is in units of Table data, not Row data, so it triggers content update notifications whenever data in the Table is updated. Room does not know which data in the table is updated, so it refires the Query operation defined in the DAO. You can use operators of Flow, such as distinctUntilChanged, to ensure that you are notified only when there is an update to the data you are interested in.

@Dao
abstract class DoggosDao {
    @Query("SELECT * FROM Dog WHERE name = :name")
    abstract fun getDog(name: String): Flow<Dog>
    fun getDogDistinctUntilChanged(name:String) =   
           getDog(name).distinctUntilChanged()
}
Copy the code

It is recommended that you do observable reads through Flow to get notifications of data updates in the database! You can use Coroutine and Flow throughout your application, as well as other Coroutine features supported in the Jetpack library, such as: Lifecycle -aware coroutine scopes, suspend lifecycle-aware coroutines, Also includes Flow to LiveData operations.

For more examples of using Flow, see our previous post on best practices based on Android Developer Summit apps.