如何在主线程上使用Kotlin Coroutines等待()



我刚刚开始学习kotlin coroutines,并试图模拟一些长时间的api-calls,并在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())
        }
    }
}

我不明白如何将launch方法与main上下文使用。

不幸的是,我找不到有关在Coroutines官方教程中提供某些特定线程的结果的任何内容。

edit

还请参见Kotlin Repo

中的官方示例

您需要实现连续界面,该接口将呼叫到Android UI线程和Coroutine Context

例如。(从这里)

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)
}

然后尝试:

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

更多信息:

https://medium.com/@macastiblancot/android-coroutines-coroutines-getting-rid-of-runonuithreadhread-and-callbacks-callbacks-cleaner-threaner-threadhardling-and-more-234c0a9bd8eb#.r2buf5e6h

您应在kotlinx.coroutines项目的kotlinx-coroutines-android模块中用UI上下文中的< NEED UI thread here >替换CC_3。它的用法在UI编程指南中用Coroutines进行了解释。

首先包括为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"
    }
  }
}

然后您可以自由使用 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)
    }
}

对于那些使用 kotlin实验中使用 kotlin实验的人

到其他项目模块 - 请记住,当您使用Kotlin实验父母模块/项目时,必须接受 kotlin实验

kotlin {
    experimental {
        coroutines "enable"
    }
  }

有几个有用的工具可以用于在Activity/Fragment中长时间运行API呼叫的目的。因此,基本上,如果您想并行运行两个长期运行的任务,并且两者都完成后更新UI,则可以下一步进行:

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-是CoroutineScope,默认情况下它具有Dispatchers.Main上下文,这意味着我们可以在launch块中更新UI。对于LifecycleScope,使用androidx.lifecycle:lifecycle-runtime-ktx:2.4.0或更高。

lifecycleScope.async(Dispatchers.Default)-此处Dispatchers.Default用作Coroutine的上下文,使async块在背景线程中运行。

anko有一个包装器可以非常简单 - 请参阅:https://github.com/kotlin/anko/wiki/anko-coroutines

private fun doCallAsync() = async(UI) {
    val user = bg { getUser() }
    val name = user.await().name
    val nameView = findViewById(R.id.name) as TextView
    nameView.text = name;
}

此答案可能是OP问题之后的2.5岁,但在类似情况下仍可能会帮助其他人。

最初的目标可以比上面接受的答案要简单得多,而无需使用异步/等待(语句1、2和3,将按照预期执行其相关延迟):

override fun onCreate(savedInstanceState: Bundle?) {
    :
    :
    :
    :
    GlobalScope.launch(Dispatchers.Main) {
    val aVal = a()   // statement 1
    val bVal = b()   // statement 2
    resultTV.setText((aVal * bVal).toString())    // statement 3
    }
    :
    :
}
suspend fun a(): Int {
    delay(1_000L)
    return 6
}
suspend fun b(): Int {
    delay(1_000L)
    return 7
}

相关内容

  • 没有找到相关文章

最新更新