如何在 firebase 数据库中使用 kotlin 协程

Posted

技术标签:

【中文标题】如何在 firebase 数据库中使用 kotlin 协程【英文标题】:How to use kotlin coroutines in firebase database 【发布时间】:2019-08-07 19:43:11 【问题描述】:

我正在尝试使用 firestore 和协程访问聊天室。

    fun getOwner() 
        runBlocking 
            var de = async(Dispatchers.IO) 
                firestore.collection("Chat").document("cF7DrENgQ4noWjr3SxKX").get()
            
            var result = de.await().result
        

但我得到这样的错误:

E/androidRuntime: FATAL EXCEPTION: Timer-0
    Process: com.example.map_fetchuser_trest, PID: 19329
    java.lang.IllegalStateException: Task is not yet complete
        at com.google.android.gms.common.internal.Preconditions.checkState(Unknown Source:29)
        at com.google.android.gms.tasks.zzu.zzb(Unknown Source:121)
        at com.google.android.gms.tasks.zzu.getResult(Unknown Source:12)
        at com.example.map_fetchuser_trest.model.Repository$getOwner$1.invokeSuspend(Repository.kt:53)

如何获取聊天文件?当我使用下面的 origin api 时,我可以访问聊天室文档。

        firestore.collection("Chat").document(
            "cF7DrENgQ4noWjr3SxKX"
        ).get().addOnCompleteListener  task ->
            if (task.isSuccessful) 
                val chatDTO = task.result?.toObject(Appointment::class.java)
            
        

【问题讨论】:

【参考方案1】:
 val db = FirebaseFirestore.getInstance()
override suspend fun saveBinToDB(bin: Bin): Result<Unit> 
    lateinit var result:Result<Unit>
    db.collection("bins")
        .add(bin)
        .addOnSuccessListener  documentReference ->
            Log.d(TAG, "DocumentSnapshot written with ID: $documentReference.id")
            result = Result.Success(Unit)
        
        .addOnFailureListener  e ->
            Log.w(TAG, "Error adding document", e)
            result = Result.Error(Exception())
        
        .await()
    return result


implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.7"

【讨论】:

当,这就是我要找的。有很多关于使用 Kotlin 与 Firestore 交互的示例,并且大多数(如果不是全部)使用 await() 函数。该 jetbrains 库的实现正是您所需要的。【参考方案2】:

第一个代码sn-p中runBlocking..的用法如下:runBlocking函数阻塞来执行参数lambda代码(而lambda代码会在里面挂起)。它给人的感觉更少。

您可能希望使用launch.. 函数启动一个协程,并使用withContext(Dispatchers.Main).. 让该块在 UI 线程中执行,例如显示获取的结果。你也可以在你的活动类中实现CoroutineScope

第一步 - 您需要将 Firebase API 调用转换为挂起函数。可以使用 suspendCoroutine.. 函数来完成(在 kotlinx.coroutines 库中还有更多函数,例如 suspendCancellableCoroutine..

有一个与 Google Play 服务集成的库,提供对 Firebase 的支持https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-play-services

【讨论】:

【参考方案3】:

Task 是人们等待的东西,但你将它包裹在另一层 async 中。删除async:

fun getOwner() 
    runBlocking 
        var de =  firestore.collection("Chat").document("cF7DrENgQ4noWjr3SxKX").get()
        var result = de.await().result
    

但是,通过使用runBlocking(),您已经在自己的脚下开了一枪,并且编写了只是正式使用异步 API 而没有好的效果的阻塞代码。

要真正从中受益,你必须拥有一个

suspend fun getOwner() = firestore
     .collection("Chat")
     .document("cF7DrENgQ4noWjr3SxKX")
     .get()
     .await()
     .result

launch 在你调用它的地方有一个协程:

launch 
    val owner = getOwner()
    // update the GUI

这假设您从一个 CoroutineScope 的对象调用 launch

【讨论】:

如果我删除“async”,那么suspend func中的“.await()”函数是从哪里来的? 您需要在org.jetbrains.kotlinx:kotlinx-coroutines-play-services 中定义的Task.await() 不可能使用相同的概念(协同程序中的挂起函数)并且还具有firestore实时功能,对吧?我问这个是因为答案中显示的代码是一个很好的例子,说明了在我们必须多次查询集合并合并由于 Firestore 查询限制而导致的结果。但是,再一次,有没有办法在保持实时功能的同时做呢?或者,有没有办法在不存在回调地狱问题的情况下进行多次调用? 有人能告诉我await() 函数是在哪个调度程序中调用的吗? Dispatchers.IODispatchers.Main 之间调用 .await() 是否有任何性能差异 @Andrew await() 的行为与任何其他可挂起函数一样,并在当前调度程序中执行。它在哪里执行与运行任务本身的代码在哪里执行无关。正如它的名字所说,它除了等待什么都不做。因此,您应该让它使用Main 调度程序。

以上是关于如何在 firebase 数据库中使用 kotlin 协程的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Firebase 中加密用户数据

如何在 Firebase 中使用离线用户更新库存?

如何在不使用 Firebase 身份验证的情况下保护 Firebase 存储? (下一个)

如何在新的 Firebase 控制台中检查数据库和存储使用情况

如何使用 Cloud Functions for Firebase 更新 Firebase 实时数据库中的值

如何使用命令行部署 Firebase 数据库安全规则?