Search code examples
kotlinkotlin-android-extensionsmutablelivedata

Run code in main thread when IO thread dispatch completes?


I'm working with livedata. I want to run some arbitrary code in IO and then once that has completed, run some arbitrary code in the Main thread.

In JavaScript, you can accomplish something like this by chaining promises together. I know Kotlin is different, but that's at least a framework I'm coming from that I understand.

I have a function that will sometimes be called from Main and sometimes from IO, but it requires no special IO features itself. From within class VM: ViewModel():

private val mState = MyState() // data class w/property `a`
val myLiveData<MyState> = MutableLiveData(mState)

fun setVal(a: MyVal) {
    mState = mState.copy(a=a)
    myLiveData.value = mState
}

fun buttonClickHandler(a: MyVal) {
    setVal(a) // Can execute in Main
}

fun getValFromDb() {
    viewModelScope.launch(Dispatchers.IO) {
        val a: MyVal = fetchFromDb()
        setVal(a) // Error! Cannot call setValue from background thread!
    }
}

Seems to me the obvious way would be to execute val a = fetchFromDb() from IO and then pull setVal(a) out of that block and into Main.

Is there a way to accomplish this? I don't see a conceptual reason why this feature could not exist. Is there some idea like

doAsyncThatReturnsValue(Dispatchers.IO) { fetchFromDb()}
    .then(previousBlockReturnVal, Dispatchers.Main) { doInMain() }

that could be run in a ViewModel?

Please substitute "coroutine" for "thread" wherever appropriate above. :)


Solution

  • Launch is fine. You just have to switch around the dispatchers and use withContext:

    fun getValFromDb() {
        // run this coroutine on main thread
        viewModelScope.launch(Dispatchers.Main) {
            // obtain result by running given block on IO thread
            // suspends coroutine until it's ready (without blocking the main thread)
            val a: MyVal = withContext(Dispatchers.IO){ fetchFromDb() }
            // executed on main thread
            setVal(a) 
        }
    }