【问题标题】:How to transform an Android Task to a Kotlin Deferred?如何将 Android 任务转换为 Kotlin Deferred?
【发布时间】:2018-11-01 13:45:57
【问题描述】:

Firebase 匿名登录返回 task(基本上是 Google promise implementation):

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

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

  • 这是一个suspend函数,等待task完成

    • suspend fun signInAnonymous(): Unit
  • 它返回一个Deferred 对象,异步传递结果

    • fun signInAnonymous() : Deferred

【问题讨论】:

    标签: android firebase kotlin kotlinx.coroutines


    【解决方案1】:

    将此添加到 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()
    }
    

    【讨论】:

      【解决方案2】:

      kotlinx.coroutines.tasks 包现在包含以下实用功能:

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

      来自docs

      等待任务完成而不阻塞线程。
      这个暂停功能是可以取消的。
      如果当前协程的Job在此挂起函数等待期间被取消或完成,则此函数停止等待完成阶段并立即以CancellationException恢复。

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

      来自docs

      将此任务转换为Deferred 的实例。
      如果任务被取消,那么产生的 deferred 也将被取消。


      所以你可以这样做:

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

      或:

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

      【讨论】:

      【解决方案3】:

      基于this GitHub library,这里有一种方法可以将Task 转换为"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
      }
      

      【讨论】:

      • 如何从这个函数返回值? @zsmb13 这两个函数都需要在异步内部调用。例如:val tokenFirebase: String = FirebaseInstanceId.getInstance().instanceId.asDeferred().await().tokenval tokenFirebase: String = FirebaseInstanceId.getInstance().instanceId.await().token // await() 显示警告,而不是:暂停函数 'await' 只能从协程或其他暂停函数中调用
      • 是的,Deferred 上的 await 和我的答案中提供的 await 都需要在协程中调用,因为它们正在暂停功能。我不确定你在问什么。如果您询问如何启动协程,您应该看到 coroutine guide
      • 就我而言,我想从 firebase 获取令牌,但如何从异步协程中返回值。我试过这个:val newToken: String by lazy { val token = with(FirebaseInstanceId.getInstance().instanceId) { /*GlobalScope.async { asDeferred().await() }*/ // for what? we await() this? if (asDeferred().isCompleted &amp;&amp; result != null &amp;&amp; isSuccessful) result?.token ?: "" else "" } token } @zsmb13 但仍然返回空白或空字符串,还是我使用了错误的方法?谢谢
      【解决方案4】:

      要将其转换为可用于协程的函数,我将使用 Tasks API 中的 Tasks.await() 函数:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-30
        • 2021-08-22
        • 2019-03-12
        相关资源
        最近更新 更多