在Task <t>完成之前,正确暂停协程的方法

时间:2018-05-13 11:49:41

标签: android kotlin kotlin-coroutines

我最近潜入Kotlin协程 由于我使用了很多Google的库,因此大部分工作都是在Task

中完成的

目前我正在使用此扩展来暂停协程

suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
    task.addOnCompleteListener { task ->
        if (task.isSuccessful) {
            continuation.resume(task.result)
        } else {
            continuation.resumeWithException(task.exception!!)
        }
    }
}

但是最近我看到了这样的用法

suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
    try {
        val result = Tasks.await(task)
        continuation.resume(result)
    } catch (e: Exception) {
        continuation.resumeWithException(e)
    }
}

有什么区别,哪一个是正确的?

UPD:第二个例子不起作用,idk为什么

2 个答案:

答案 0 :(得分:10)

传递给suspendCoroutine { ... }的代码块不应该阻塞正在调用它的线程,允许协程被挂起。这样,实际线程可以用于其他任务。这是一个关键功能,允许Kotlin协程扩展并在单个UI线程上运行多个协同程序。

第一个示例正确执行,因为它调用task.addOnCompleteListener (see docs)(它只是添加一个侦听器并立即返回。这就是第一个正常工作的原因。

第二个示例使用Tasks.await(task) (see docs) 阻止调用它的线程,并且在任务完成之前不会返回,因此它不允许协程正确挂起。

答案 1 :(得分:1)

您的第二个示例是更传统的阻止功能。

在典型的Java多线程世界中,您使用类似的函数来阻塞线程,直到结果返回。

同样在典型的Java中,有一种称为反应式编程的新范例,它允许您在结果返回之前避免阻塞线程。您可以提供一个在结果到达时执行的第一类函数,而不是仅仅等待结果。这样,暂停时没有留下线程;相反,只有一个新事件附加到运行长操作的线程上,当操作完成时,该事件将触发。

封闭:

Thread one:  --* . . . ./---
Thread two:     \------*

活性:

Thread one: --*   (now free)
Thread two:    \-------*---

如果你研究反应式编程,你会发现很多深入的解释。

回到你的问题,这两种看似相同的方式是Java范式转变的结果。当然,既然我们在这里与Coroutines合作,我们就不必担心的阻塞问题;该语言通过暂停而不是阻止来处理它。因此,反应性编程的许多优点都可以在语言层面上得到解决。

然而,这种范式仍然有其优点,所以即使它不像其他地方那样严格必要,看到人们继续以这种模式工作也是有道理的。