Kotlin: structure for sequential coroutines

1.1k views Asked by At

I am building a library manager app for Android and am trying to build a view to show the hierarchy of "collections" (i.e. folders) from the current collection up to root.

The database is implemented with Room (accessed indirectly via the collectionRepo). Each Collection object has a unique key and knows its parent's key.

What I'm trying to do:

  1. Given the current collection's key, build the Collection hierarchy up to root.
  2. After this is complete, build a UI rendering of the hierarchy.

This seems like it should be pretty straightforward with Kotlin's coroutines, but I haven't been able to get the coroutines structured correctly.

Since each call to collectionRepo.getCollection(curKey) ultimately does a Room db query, it has to be off the main thread. But I need to wait for the query to return in order to know the parentKey needed for the subsequent query.

private suspend fun buildBackstackView() {

    var curKey = collectionViewModel.getCurrentCollectionKey()

    coroutineScope {
        launch {
            do {
                val curCollection = when (curKey) {
                    "" -> {
                        Collection("", 0, "TOP", "") // collection's parent is root - create a fake root Collection to push to the stack
                    }
                    else -> {
                        collectionRepo.getCollection(curKey) // ultimately a Room database query
                    }
                }

                collectionStack.add(curCollection)
                curKey = curCollection.key

            } while (curKey != "")
        }
    }

    withContext(Dispatchers.Main) {

        ...build UI view using collectionStack...

    }
}

In response to Sergey's request, collectionRepo.getCollection() just passes the request along to a DAO method that defines a Room query.

@Query("SELECT * FROM collections WHERE `key` = :colKey LIMIT 1")
fun getCollection(colKey: String): Collection
1

There are 1 answers

2
Sergio On

Please check next code, it should help you:

    // creating local scope for coroutines
    private var job: Job = Job()
    private val scope = CoroutineScope(Dispatchers.Main + job)     

    // call this to cancel job when you don't need it anymore, for example in Activity.onDestroy() method
    fun cancelJob() {
        job.cancel()
    }

    private fun buildBackstackView() {
        var curKey = collectionViewModel.getCurrentCollectionKey()

        scope.launch {
            do {
                val curCollection = when (curKey) {
                    "" -> {
                        Collection("", 0, "TOP", "")
                    }
                    else -> withContext(Dispatchers.IO) { // runs in background thread suspending the coroutine
                        collectionRepo.getCollection(curKey)
                    }

                }

                collectionStack.add(curCollection)
                curKey = curCollection.key

            } while (curKey != "")

            // ...build UI view using collectionStack...
        }
    }

To use Dispatchers.Main import:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'