Kotlin Co-Routines-从Co-routine返回一个值,而不会阻塞Android主线程

时间:2018-07-10 17:38:03

标签: android kotlin kotlin-coroutines

我对Co-Routines相对陌生,我正在尝试实现Launch协同例程将完成的行为:

launch(UI) { 

     val v1 = someDeferredType 
     val v2 = v1.await()
     val v3 = v2.text

} 

在上面的示例中,v3将等待v2执行,然后在不阻塞主线程的情况下运行。尽管这很棒,但在我的“呼叫活动/片段”中引入了“延迟类型”和“例程”逻辑。

我想让我的“活动/片段”免于特定的实现细节,例如:

 fun getResponseString() : String {

     launch(UI) { 

           val v1 = someDeferredType 
           val v2 = v1.await()
           val v3 = v2.text

      } 

      return v3 //This is the actual String which I need to be returned
 }

这样我就可以像我活动中的常规函数​​一样调用getResponseString()。

到目前为止,我遇到的唯一选择是使用runBlocking协同例程,但这与启动不同,那就是完全阻塞了主线程。

也许我丢失了某些东西,或者无法在Kotlin中使用协例程来做类似的事情?

2 个答案:

答案 0 :(得分:1)

您无法从诸如getResponseString之类的常规函数​​中返回异步操作的结果。常规函数不具有这种执行挂起功能,而不会阻塞被调用的线程。这就是为什么Kotlin必须引入“暂停功能”的概念,所以您可以这样写:

suspend fun getResponseString(): String {
    val v1 = someDeferredType 
    val v2 = v1.await()
    val v3 = v2.text
    return v3
}

suspend修饰符添加到所有异步函数(必须等待某些东西但不应阻止UI线程的函数),然后仅在最顶层使用launch(UI) { ... }的想法需要启动一些自包含的异步操作。

P.S。协程也被拼写为“协程”。这是一个字,没有破折号。例如,请参见setState

答案 1 :(得分:0)

常规函数和挂起函数之间的区别不仅在于实现细节:它还改变了程序的语义。使用同步代码,您知道在执行所有其他UI事件处理程序之前将执行所有操作。您将失去异步代码的原子性,并进入事件处理程序彼此并发运行的“异步地狱”世界。

Kotlin清楚地表明了这一事实,这太棒了:只要您的代码路径未输入协程生成器,您就知道您具有原子性保证。您必须始终明智地选择丢失它的位置,因为一旦这样做,所有其他程序的复杂性就会增加。

写作时

override fun onSomething() {
   val v0 = getV0()
   launch(UI) { 
        val v1 = getV1Async()
        val v2 = v1.await()
        useV2ToUpdateTheGUI(v2)
   }
   val v4 = getV4()
}

这将是您的处理程序代码的执行顺序:

  1. v0 = getV0()
  2. v4 = getV4()
  3. onSomething处理程序返回
  4. 其他一些处理程序运行
  5. v1 = getV1Async()
  6. 其他一些处理程序运行
  7. v2 = v1.await()
  8. useV2ToUpdateTheGUI(v2)

您的onSomething处理程序返回上面的3后,将运行不受控制的未知代码量。最臭名昭著的是,您自己的处理程序将运行,并且不允许任何人假设onSomething中启动的所有操作均已完成。每当您想使用v2的值时,都必须添加代码来确定尚不可用的情况。

您可以将launch调用后面的fun隐藏在onSomething后面,但是随后您必须在注释/文档中仔细解释该功能只会触发并发任务。自然,您将无法在处理程序主体中使用该任务的结果。

我的经验是,您应该在处理程序中显式地拥有launch(UI),或者应该将方法命名为launchFooBar()