How to use Kotlin coroutines await() on main thread
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
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.
First of all include right library designed for Android
build.gradle
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android{
...
dependencies{
...
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.19.3"
}
kotlin {
experimental {
coroutines "enable"
}
}
}
Then you are free to use UI
suspend private fun getFilteredGList(enumList: List<EnumXXX>) = mList.filter {
...
}
private fun filter() {
val enumList = listOf(EnumX1, EnumX2)
launch(UI){
val filteredList = getFilteredList(enumList)
setMarkersOnMap(filteredList)
}
}
For those that expose project using kotlin experimental in gradle
as .aar or .apk to other projects module - Just remember, when You're using kotlin experimental parent modules/project have to accept kotlin experimental as well
kotlin {
experimental {
coroutines "enable"
}
}
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.