【问题标题】:How To Use Async/Await/Coroutines In OnCompleteListener Firebase如何在 OnCompleteListener Firebase 中使用 Async/Await/Coroutines
【发布时间】:2021-01-13 05:21:24
【问题描述】:

我正在构建一个使用 Firebase 做两件事的客户端应用程序:

  • 用户认证
  • 使用实时数据库

我已经成功地在我的客户端和后端服务器上正确设置了所有内容(使用 Firebase 的 Admin SDK),并且能够正确验证用户并允许他们读取/写入数据库。

我也在使用 Retrofit2 将请求从客户端发送到后端。

作为允许用户访问数据库的一部分,需要将用户的令牌发送到后端以便验证用户。

为此,我有以下逻辑:

val user = FirebaseAuth.getInstance().currentUser

    if (user != null) {
        user.getIdToken(false).addOnCompleteListener {
            if (it.isSuccessful) {
                val token = it.result?.token
                //retrofit logic to send request happens from here
            }
       }

如您所见,获取用户的 Id 令牌是一个异步调用,在我拥有的当前代码库中,我对后端的每个调用(重复)都有这个代码块。

我想知道如何将这个 sn-p 导出到一个函数(可能是一个挂起方法?),以便每次调用后端时都可以重用它

我在网上搜索并看到了很多 SO 问题,但没有一个适合这种情况。 我曾考虑过传入一个回调,但我有几个与后端通信的方法,每个方法都需要不同的回调方法。

我正在寻找的解决方案如下所示:

fun fetchDataFromDB() {
  getIdTokenForUser()
  //wait till it finishes and then
  //perform request to DB
}

fun updateDataInDB() {
  getIdTokenForUser()
  //wait till it finishes and then
  //perform request to DB
}

//......

我曾尝试阅读和实施协程,但我缺乏正确执行此操作的知识。

编辑

感谢@Doug Stevenson 的回答和指导,我设法构建了以下内容:

private suspend fun getUserIdToken(user: FirebaseUser) = coroutineScope {

    val job = async {
        user.getIdToken(false).result?.token
    }
    job.await()
}

我以这种方式使用它:

fun updateDB(context: Context) = runBlocking {

    val user = FirebaseAuth.getInstance().currentUser

    if (user != null) {
        val token = getUserIdToken(user)
    }

  }

这是正确的方法吗?因为下面给出的答案提供了不同的实现方式。

【问题讨论】:

  • 根据您对我的回答的 cmets,看来您实际上并没有添加 kotlinx-coroutines-play-services 库或导入我谈到的扩展功能。无需使用coroutineScope。一个普通的suspend fun 就可以了。
  • @DougStevenson - 相反。我已将依赖项添加到我的 build.gradle 文件中,但您在答案中的导入不存在(正如我在下面的评论中提到的)。移除到 coroutineScope 时,async 关键字出现编译错误。
  • 这种方法的问题在于,每次调用该函数时都会创建一个协程作用域,因此您无法真正获得结构化并发 Roman Elizarov(kotlinx-coroutines 的主要贡献者之一)。我的方法是基于他的,你应该从一个生命周期绑定的协程范围内调用这样的挂起函数,比如你的ActivityFragment 中的lifecycleScopeViewModelAndroidViewModel 中的viewModelScope 中的任何一个。因此,每当组件的生命周期结束时,(onDestroy 或 onCleared)任何挂起的作业也会取消,并且您不会泄漏协程 :)
  • 顺便说一句@Doug 的方法也是正确的。他建议您安装一个已经为您在普通 java 方法上执行此类包装的依赖项。但是在您的实现中,您在函数调用时创建了一个 coroutineScope,这就是我认为您应该更改的地方

标签: android firebase kotlin firebase-authentication kotlin-coroutines


【解决方案1】:

getIdToken是异步返回一个Task对象。如果您想在 Kotlin 协程中使用 Task 对象,您可以使用库 kotlinx-coroutines-play-services 将扩展方法 await() 添加到 Task 中,使其可在协程中使用。有了它,你可以写这样的东西:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.9"
import kotlinx.coroutines.tasks.await

suspend fun getIdTokenForUser(user: FirebaseUser): GetTokenResult {
    return try {
        user.getIdToken(false).await()
    }
    catch (e: Exception) {
        // handle error
    }
}

您可能必须在此处更新类型 - 我没有尝试编译或测试它。

另见:

【讨论】:

  • 感谢您的回答 - 但是当我尝试使用您的代码时,我得到了一个未解决的 await() 参考。我已经导入了协程库,通过网上搜索了一下,我看到你添加的代码可能需要包装在GlobalScope.async?我尝试了不同的变体,但仍然没有成功。
  • 你必须从我链接的库中导入扩展函数才能使用它。
  • 链接已损坏,因此我修复了它,但我仍然无法找到您所指的导入。您是否尝试复制您在 Android Studio 中提供的代码 sn-p 并查看它是否可以编译?
  • 哪个链接坏了?我的答案中的所有链接对我来说都可以。其他问题的链接都具有相似的代码,并且基本上与我所说的相同。第一个甚至显示了导入:import kotlinx.coroutines.tasks.await
  • 您为 kotlinx-coroutines-play-services 提供的链接已损坏。您可以查看编辑历史记录并查看我的更正。此外,我使用的是 1.3.9 版本的依赖项,并且没有您编写的导入路径。没有任务包。 Await 可以在 Deferred 接口内部找到。我已经设法找到了一个解决方案(请参阅我的问题的编辑)。
【解决方案2】:

为了从基于回调的 API 出发,如下所示:


val myCallback = object : ServiceCallback() {
    override fun onResult(theobject: Something) {
        // your callback code here
    }
    override fun onFailure(ex: Throwable) {
         // error handling
    }

}

theService.enqueue(callback)

您可以使用suspendCoroutine

它的作用是暂停执行,直到回调满足继续执行。所以你可以像下面这样写一个KTX:

suspend fun Service.getSomething(): Something = suspendCoroutine{ cont ->
    val callback = object : ServiceCallback(){
         override fun onSuccess(data: Something): Unit = cont.resume(data)
         override fun onFailure(ex: Throwable): Unit = cont.resume(ex)
    }
    this.enqueue(callback)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-08
    • 2017-09-28
    • 2021-10-30
    • 2019-07-20
    • 2021-10-10
    • 2019-07-24
    • 1970-01-01
    相关资源
    最近更新 更多