如何在 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 协程的主要内容,如果未能解决你的问题,请参考以下文章