Kotlin联合例程-在不阻塞Android主线程的情况下从联合例程返回一个值



我对协同例程相对较新,我正在尝试获得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中使用Co例程不可能做这样的事情吗?

不能从像getResponseString这样的常规函数返回异步操作的结果。常规函数没有这种在不阻塞调用线程的情况下暂停执行的能力。这就是为什么Kotlin必须引入"暂停函数"的概念,所以你可以写:

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

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

附言:coroutines也被拼写为"协同出游"。这是一个单词,没有破折号。例如,请参见维基百科。

正则函数和挂起函数之间的区别不仅仅是实现细节:它改变了程序的语义。使用同步代码,您知道它所做的所有操作都是在调用任何其他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调用隐藏在onSomething调用的fun后面,但您必须在注释/文档中仔细解释,此函数只是启动一个并发任务。当然,您将无法在处理程序的主体中使用该任务的结果。

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

最新更新