avatarFlorina Muntenescu

Summary

The provided content discusses the integration of Kotlin's Flow API in Android's Room library for reactive database queries, allowing for real-time updates in the application's UI.

Abstract

The web content details the enhancement of Room, an Android persistence library, with support for Kotlin's Flow API, which was introduced in Room 2.2. This integration enables developers to observe database changes in real-time, ensuring that the application's UI remains synchronized with the underlying data. The article explains how Flow can be used to receive updates for all entries in a database table, as well as for individual items, by using queries in the DAO (Data Access Object). It also addresses the challenge of SQLite's table-level notifications, which can result in unnecessary updates, and suggests using Flow operators like distinctUntilChanged to filter out unchanged data, optimizing performance and user experience. The article concludes by encouraging developers to leverage the full potential of coroutines and Flow across their applications, in conjunction with other Jetpack libraries for a seamless, reactive data handling experience.

Opinions

  • The author positively views the addition of Flow support in Room, highlighting its benefits for real-time data synchronization.
  • There is an emphasis on the importance of keeping the UI updated with the latest database changes, which Flow facilitates efficiently.
  • The article suggests that using Flow with Room is a significant step forward in Android development, streamlining the process of data observation and reactivity.
  • The author acknowledges the limitation of SQLite's table-level notifications and provides a practical solution using Flow's distinctUntilChanged operator to mitigate performance issues.
  • The overall tone conveys enthusiasm for the continued improvement of Room's coroutine support, suggesting that it enhances the developer experience and application performance.
Flow support in Room

Room 🔗 Flow

Coroutines support in Room has been increasing at every release: Room 2.1 added coroutines support for one-shot read / write operations and with Room 2.2 we now have support for observable reads with Flow enabling you to get notified of changes in your database.

Room async queries support

Flow in action

Let’s say that we have a database of dogs, where the name is the primary key, therefore, we can’t have 2 dogs with the same name in the database.

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

To display a full list of dogs with all of their info we’d write a query like this in our DAO:

@Query("SELECT * FROM Dog")
fun getAllDogs(): List<Dog>

Because the barking volume of a dog can change over time and we want to make sure that our UI is up to date, then we want to get notified of every change that happens in the Dogs table: new dogs added, dogs removed or updated. To achieve this, we update the query to return Flow:

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

Like this, whenever a dog in the database is updated, then the entire list of dogs is emitted again. For example, let’s say that we have the following data in the database:

(Frida, 11, 3)
(Bandit, 12, 5)

When we first call getAllDogs, our Flow will emit:

[(Frida, 11, 3), (Bandit, 12, 5)]

If Bandit gets excited and his bark volume is updated to 6: (Bandit, 12, 6), the Flow will emit again, with the entire content of the dogs table with the latest values:

[(Frida, 11, 3), (Bandit, 12, 6)]

Now let’s say that we can open a dog details in a new screen. Because we also want to ensure we always have the latest data on the dog and get updates in real time, we return a Flow:

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

Now, if we call getDog("Frida"), Flow will return one object: (Frida, 11, 3).

Whenever any changes are made to the table, independent of which row is changed, the query will be re-triggered and the Flow will emit again. So if Frida gets updated, we will receive the latest info.

However, this behaviour of the database also means that if we update an unrelated row, like Bandit, our Flow will emit again, with the same result: (Frida, 11, 3). Because SQLite database triggers only allow notifications at table level and not at row level, Room can’t know what exactly has changed in the table data, therefore it re-triggers the query defined in the DAO. In your code, use Flow operators like distinctUntilChanged, to ensure that you only get notified when the data you’re interested in has changed:

@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()
}

Start getting notified of the changes in your database by using observable reads with Flow! Together with other coroutines support added in Jetpack libraries like lifecycle-aware coroutine scopes, suspend lifecycle-aware coroutines or the transformation from Flow to LiveData, you can now use coroutines and Flow throughout your entire application.

To find out more about using Flow in your app, check out this post on the lessons learned while using Flow in the Android Dev Summit 2019 app.

Android
Android App Development
Kotlin
Room
Database
Recommended from ReadMedium
avatarNine Pages Of My Life
17 Kotlin Advanced Idioms

🎯Index

5 min read