11

我刚开始学习 Kotlin 协程,并试图通过在 UI 上显示结果来模拟一些长时间的 API 调用:

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 的官方教程中找不到任何关于为某些特定线程提供结果的信息。

4

6 回答 6

10

编辑

另请参阅Kotlin repo 中的官方示例

您需要实现对 Android UI 线程和协程上下文进行回调的Continuation接口

例如(从这里

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-getting-rid-of-runonuithread-and-callbacks-cleaner-thread-handling-and-more-234c0a9bd8eb#.r2buf5e6h

于 2017-03-03T03:28:01.523 回答
9

您应使用kotlinx.coroutines项目模块中的上下文替换< NEED UI thread here >您的代码。它的用法在Guide to UI Programming with coroutines中有很多例子。UIkotlinx-coroutines-android

于 2017-03-26T15:42:49.707 回答
4

首先包括为Android设计的正确库

构建.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)
    }

}

对于那些在.aar.apk中将使用kotlin 实验性gradle的项目公开 给其他项目模块的人 - 请记住,当您使用 kotlin 实验性父模块/项目时也必须接受kotlin experimental

kotlin {
    experimental {
        coroutines "enable"
    }
  }
于 2017-12-01T10:56:19.513 回答
0

这个答案可能是在 OP 提出问题后 2.5 年,但它仍然可以帮助处于类似情况的其他人。

在不使用 async/await 的情况下,可以以比上面接受的答案更简单的方式实现最初的目标(语句 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
}
于 2019-08-11T14:59:43.437 回答
0

有几个有用的工具可用于在Activity/中长时间运行 API 调用Fragment。因此,基本上,如果您想并行运行两个长时间运行的任务并在两者完成后更新 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用作协程的上下文,以使async块在后台线程中运行。

于 2022-02-12T13:49:47.900 回答
0

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;

}
于 2017-08-15T16:33:20.487 回答