【发布时间】: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 的主要贡献者之一)。我的方法是基于他的,你应该从一个生命周期绑定的协程范围内调用这样的挂起函数,比如你的
Activity或Fragment中的lifecycleScope或ViewModel或AndroidViewModel中的viewModelScope中的任何一个。因此,每当组件的生命周期结束时,(onDestroy 或 onCleared)任何挂起的作业也会取消,并且您不会泄漏协程 :) -
顺便说一句@Doug 的方法也是正确的。他建议您安装一个已经为您在普通 java 方法上执行此类包装的依赖项。但是在您的实现中,您在函数调用时创建了一个 coroutineScope,这就是我认为您应该更改的地方
标签: android firebase kotlin firebase-authentication kotlin-coroutines