Kotlin协程 - 在不阻塞主线程的情况下从协程中返回值 Android

3

我对协程还比较陌生,现在我正在尝试实现Launch协程的行为:

launch(UI) { 

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

} 

在上面的例子中,v3将等待v2执行,然后运行而不阻塞主线程。虽然这很好,但这会在我的调用Activity/Fragment中引入Deferred类型和协程逻辑。
我想让我的Activity/Fragment摆脱特定的实现细节,类似于这样:
 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协程,但这会完全阻塞主线程,不像launch。

也许我错过了什么,或者使用Kotlin中的协程无法做到这样的事情?


1
你不能暂停在主线程上运行的任何调用,你需要通过回调接口来解决。 - Pawel
@Pawel - 所以基本上,我要么在我的UI代码中编写启动,要么回退到接口回调机制,对吗? - Adnan Mulla
常规函数和挂起函数之间的区别不仅仅是形式上或实现细节上的,它会改变程序的语义。使用同步代码,您知道它执行的所有操作都在调用任何其他UI事件处理程序之前完成。但是,使用异步代码,您会失去这种原子性,并进入“异步地狱”的世界,在这个世界中,您的事件处理程序会并发运行。 - Marko Topolnik
2个回答

3

您不能从常规函数(例如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. 还有一点,协程的拼写是“coroutines”。这是一个单词,没有破折号。例如,请参见维基百科

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)

在 3. 中,您的 onSomething 处理程序返回后,将运行大量未知的代码。最为臭名昭著的是,您自己的处理程序运行,并且没有一个处理程序可以假设在 onSomething 中启动的所有操作都已完成。每当您想要使用 v2 的值时,您必须添加代码来决定如果它仍然没有准备好该怎么办。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接