Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Kotlin coroutines await() on main thread

I just started learning Kotlin coroutines and was trying to simulate some long time API-calls with showing the result on the UI:

class MainActivity : AppCompatActivity() {
    fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")

    override
    fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        this.setContentView(R.layout.activity_main)
        val resultTV = findViewById(R.id.text) as TextView

        val a = async(CommonPool) {
            delay(1_000L)
            6
        }

        val b = async(CommonPool) {
            delay(1_000L)
            7
        }

        launch(< NEED UI thread here >) {
            val aVal = a.await()
            val bVal = b.await()
            resultTV.setText((aVal * bVal).toString())
        }
    }
}

I don't understand how could I possibly use launch method with main context.

Unfortunately, I was not able to find anything about delivering results for some specific threads on the official tutorial for coroutines.

like image 332
Rostyslav Roshak Avatar asked Mar 02 '17 16:03

Rostyslav Roshak


3 Answers

There are a couple of useful tools that can be used for the purpose of long time running API-calls in Activity/Fragment. So basically if you want to run two long running tasks in parallel and update UI after both are finished you can do it the next way:

lifecycleScope.launch {
    // launching two tasks in parallel
    val aValDeferred = executeLongRunningTask1Async()
    val bValDeferred = executeLongRunningTask2Async()

    // wait for both of them are finished
    val aVal = aValDeferred.await()
    val bVal = bValDeferred.await()

    // update UI
    resultTV.setText((aVal * bVal).toString())
}

private fun executeLongRunningTask1Async(): Deferred<Int> = lifecycleScope.async(Dispatchers.Default) {
    delay(1_000L)
    6
}

private fun executeLongRunningTask2Async(): Deferred<Int> = lifecycleScope.async(Dispatchers.Default) {
    delay(1_000L)
    7
}

lifecycleScope - is a CoroutineScope, by default it has Dispatchers.Main context, it means we can update UI in the launch block. For LifecycleScope, use androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 or higher.

lifecycleScope.async(Dispatchers.Default) - here Dispatchers.Default is used as a context of the coroutine to have the async block running in the background thread.

like image 108
Sergey Avatar answered Oct 20 '22 15:10

Sergey


Edit:

Also see an official example in Kotlin repo

you need to implement Continuation interface which makes a callback onto Android UI thread and Coroutine context

e.g. (from here)

private class AndroidContinuation<T>(val cont: Continuation<T>) : Continuation<T> by cont {
    override fun resume(value: T) {
        if (Looper.myLooper() == Looper.getMainLooper()) cont.resume(value)
        else Handler(Looper.getMainLooper()).post { cont.resume(value) }
    }
    override fun resumeWithException(exception: Throwable) {
        if (Looper.myLooper() == Looper.getMainLooper()) cont.resumeWithException(exception)
        else Handler(Looper.getMainLooper()).post { cont.resumeWithException(exception) }
    }
}

object Android : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
    override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
        AndroidContinuation(continuation)
}

Then try:

launch(Android) {
    val aVal = a.await()
    val bVal = b.await()
    resultTV.setText((aVal * bVal).toString()) 
}

more info:

https://medium.com/@macastiblancot/android-coroutines-getting-rid-of-runonuithread-and-callbacks-cleaner-thread-handling-and-more-234c0a9bd8eb#.r2buf5e6h

like image 30
pt2121 Avatar answered Oct 20 '22 17:10

pt2121


You shall replace < NEED UI thread here > in your code with UI context from kotlinx-coroutines-android module of kotlinx.coroutines project. Its usage is explained in the Guide to UI programming with coroutines with quite a few examples.

like image 23
Roman Elizarov Avatar answered Oct 20 '22 16:10

Roman Elizarov