How to transform an Android Task to a Kotlin Deferred?

好久不见. 提交于 2019-12-18 15:55:08

问题


Firebase anonymous sign in returns a task (which is basically Google promise implementation):

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

How it would be possible create a signInAnonymous wrapper where:

  • It is a suspend function, waiting for the task completion

    • suspend fun signInAnonymous(): Unit
  • It returns a Deferred object, delivering the result asynchronously

    • fun signInAnonymous() : Deferred

回答1:


Based on this GitHub library, here's a way to transform a Task into a suspending function in the "usual" way to adapt callback based async calls to coroutines:

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"))
        }
    }
}

You can also wrap it in a Deferred of course, CompletableDeferred comes in handy here:

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
}



回答2:


The package kotlinx.coroutines.tasks now includes the follwing utility functions:

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

From the 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> { ... }

From the docs:

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


So you can just do:

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

or:

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



回答3:


To transform it into a coroutine-ready function, I would use the Tasks.await() function from the Tasks API:

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

As for Deferred, i'd stick with zsmb13's answer



来源:https://stackoverflow.com/questions/50473637/how-to-transform-an-android-task-to-a-kotlin-deferred

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!