在 Task<T> 完成之前挂起协程的正确方法

Correct way to suspend coroutine until Task<T> is complete

我最近沉迷于 Kotlin 协程 由于我使用了很多 Google 的库,大部分工作都是在 Task class

中完成的

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

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:第二个例子不工作,我知道为什么

传递给 suspendCoroutine { ... } 的代码块不应阻塞正在调用它的线程,从而允许暂停协程。这样,实际线程可以用于其他任务。这是一个关键特性,它允许 Kotlin 协程扩展 运行 多个协程,即使在单个 UI 线程上也是如此。

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

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

等待Task to complete using Kotlin Coroutines is to convert the Task object into a Deferred object by applying Task.asDeferred扩展函数的方法之一。例如,从 Firebase 数据库 中获取数据可能如下所示:

suspend fun makeRequest() {
    val task: Task<DataSnapshot> = FirebaseDatabase.getInstance().reference.get()
    val deferred: Deferred<DataSnapshot> = task.asDeferred()
    val data: Iterable<DataSnapshot> = deferred.await().children

    // ... use data
}

Task.asDeferred() 的依赖关系:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.2'

要调用 suspend 函数,我们需要启动协程:

someCoroutineScope.launch {
    makeRequest()
}

someCoroutineScopeActivityFragment 中的 CoroutineScope instance. In android it can be viewModelScope in ViewModel class and lifecycleScope,或者一些自定义的 CoroutineScope 实例。依赖项:

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'