Search code examples
androidkotlinkotlinx.coroutines

Room with Kotlin-coroutines observe db changes


So, I recently started experimentation with coroutines, I switched from Rxjava2 to coroutines, I haven't got a grasp of it yet but still, I ran into a condition where I needed to observe my database change and update the UI corresponding to that.

RxJava used to provide me with Flowables, Completeable etc. using that I would be able to observe changes in Db.

    abstract fun insert(data: SomeData): Long

    @Query("SELECT * FROM somedata_table")
    abstract fun getData(): Flowable<List<SomeData>>

So here now I used to subscribe to getData and always used to observe changes

Now Enter coroutines, I am using a suspended function with a deferred result to return my responses

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract fun insert(data: SomeData): Long

    @Query("SELECT * FROM somedata_table")
    abstract fun getData(): List<SomeData>
suspend fun getAllSomeData():Deferred<List<SomeData>>{
        return GlobalScope.async (context= coroutineContext){
            database.myDao().getData()
        }
    }

Now I have no way to listen for updates, Channels in coroutines might be the right answer? but I am not sure how to use it with Room.


Solution

  • Use Room 2.2.0 Flows and kotlin coroutines. It's contentious but I dislike LiveData as it gives you results on the UI thread. If you have to do any data parsing you'll have to push everything back to another IO thread. It's also cleaner than using channels directly as you have to do extra openSubscription().consumeEach { .. } calls every time you want to listen to events.

    Flow approach Requires the following versions:

    // this version uses coroutines and flows in their non-experimental version

    org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2
    androidx.room:room-runtime:2.2.0
    androidx.room:room-compiler:2.2.0
    

    Dao:

    @Dao
    interface MyDao {
         @Query("SELECT * FROM somedata_table")
         fun getData(): Flow<List<SomeData>>
    }
    

    class to do observation:

    launch {
       dao.getData().collect { data ->
        //handle data here
       }
    }
    

    if your calling class is not itself a CoroutineScope you'd have to call launch with the context of something that is. That can be GlobalScope or some other class you create. Here I'm using lifecycleScope assuming we're in an Activity class.

    lifecycleScope.launch {
       dao.getData().collect { data ->
        //handle data here
       }
    }
    

    the collect lambda will receive every udpate to the table much like an Rx onNext call.