如何将 Android Task 转换为 Kotlin Deferred?

How to transform an Android Task to a Kotlin Deferred?

Firebase 匿名登录 returns task (which is basically Google promise implementation):

val task:Task<AuthResult> = FirebaseAuth.getInstance().signInAnonymously()

如何创建一个 signInAnonymous 包装器,其中:

基于this GitHub library, here's a way to transform a Task into a suspending function in the "usual" way以适应基于回调的异步调用协程:

suspend fun <T> Task<T>.await(): T = suspendCoroutine { continuation ->
    addOnCompleteListener { task ->
        if (task.isSuccessful) {
            continuation.resume(task.result)
        } else {
            continuation.resumeWithException(task.exception ?: RuntimeException("Unknown task exception"))
        }
    }
}

当然也可以用Deferred包起来,CompletableDeferred这里就派上用场了:

fun <T> Task<T>.asDeferred(): Deferred<T> {
    val deferred = CompletableDeferred<T>()

    deferred.invokeOnCompletion {
        if (deferred.isCancelled) {
            // optional, handle coroutine cancellation however you'd like here
        }
    }

    this.addOnSuccessListener { result -> deferred.complete(result) }
    this.addOnFailureListener { exception -> deferred.completeExceptionally(exception) }

    return deferred
}

要将其转换为协程就绪函数,我会使用任务 API:

中的 Tasks.await() 函数
suspend fun FirebaseAuth.signInAnonymouslyAwait(): AuthResult {
    return Tasks.await(this.signInAnonymously())
}

至于延迟,我会坚持 zsmb13 的回答

软件包 kotlinx.coroutines.tasks 现在包含以下实用函数:

public suspend fun <T> Task<T>.await(): T { ... }

来自docs

Awaits for completion of the task without blocking a thread.
This suspending function is cancellable.
If the Job of the current coroutine is cancelled or completed while this suspending function is waiting, this function stops waiting for the completion stage and immediately resumes with CancellationException.

public fun <T> Task<T>.asDeferred(): Deferred<T> { ... }

来自docs

Converts this task to an instance of Deferred.
If task is cancelled then resulting deferred will be cancelled as well.


所以你可以这样做:

suspend fun signInAnonymouslyAwait(): AuthResult {
    return FirebaseAuth.getInstance().signInAnonymously().await()
}

或:

fun signInAnonymouslyDeferred(): Deferred<AuthResult> {
    return FirebaseAuth.getInstance().signInAnonymously().asDeferred()
}

将此添加到 gradle

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

然后你可以这样使用它:

suspend fun login(email: String, pass: String) {
    FirebaseAuth.getInstance().signInWithEmailAndPassword(email, pass).await()
}