【问题标题】:Firebase Realtime Database wait until the data is retrievedFirebase 实时数据库等到检索到数据
【发布时间】:2022-02-19 05:49:26
【问题描述】:

我有一个包含我的 Firebase 实时数据库键的地图,并且想要检索相应的键数据并将其放入结果数据列表中。如何按顺序执行循环?基本上,阻塞 Firebase 监听器直到它得到结果,然后才迭代到循环中的下一个键。

fun functionA() {

     val resultFileDataList = List<DataSnapshot>()
     for ((key, value) in filesMap) {
           val dbRef = database.child("files").child(key)
           dbRef.addListenerForSingleValueEvent(object : ValueEventListener {
                 override fun onCancelled(p0: DatabaseError) {}
                 override fun onDataChange(dataSnapshot: DataSnapshot) {
                        resultFileDataList.add(dataSnapshot)
                      }
                })
      }

    callFunctionB() // call this function only after all the data in the loop above is retrieved
}

我尝试了runBlocking {},但没有成功。

【问题讨论】:

  • 您知道添加所有文件后列表中应该存在多少元素吗?是固定数字吗?

标签: android firebase kotlin firebase-realtime-database google-cloud-platform


【解决方案1】:

您可以通过使用任务来实现它。 Tasks.whenall() 将等待所有任务完成。

fun functionA() {

    val taskList = mutableListOf<Task<DataSnapshot>>()
    val resultFileDataList = List<DataSnapshot>()

    for ((key, value) in filesMap) {
        val databaseReferenceTask: Task<DataSnapshot> = database.child("files").child(key).get()
        taskList.add(databaseReferenceTask)

        val resultTask = Tasks.whenAll(taskList)
        resultTask.addOnCompleteListener {
            for (task in taskList) {
                val snapshotKey: String? = task.result.key
                val snapShotValue = task.result
            }
            
            callFunctionB() 
        }
    }
}

【讨论】:

    【解决方案2】:

    既然您使用的是 Kotlin,那么最简单的解决方案就是使用 Kotlin Coroutines。这样,您可以使用挂起函数并为每个读取操作调用等待。为此,请查看以下文章:

    如果你需要pipeline the requests over its existing connection,那么你应该考虑使用kotlinx-coroutines-play-services,在这种情况下你可以使用awaitAll()函数。

    【讨论】:

    • 我正在寻找流水线请求,但您共享的示例链接适用于 iOS。你能分享类似的android代码吗?我尝试搜索它,但找不到任何适用于 android 的内容。
    • 我找到了一个有用的答案here。试一试,告诉我它是否有效。
    【解决方案3】:

    这是一种方法:

    suspend fun functionA() = suspendCoroutine<List<DataSnapshot>>{ continuation ->
    
        val resultFileDataList = mutableListOf<DataSnapshot>()
        for ((key, value) in filesMap) {
            val dbRef = database.child("files").child(key)
            dbRef.addListenerForSingleValueEvent(object : ValueEventListener {
                override fun onCancelled(p0: DatabaseError) {}
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    resultFileDataList.add(dataSnapshot)
                    if(resultFileDataList.size == fileMaps.size){
                        continuation.resume(resultFileDataList)
                    }
                }
            })
        }
    }
    

    然后你可以像这样在任何你想要的地方调用函数:

    CoroutineScope(Dispatchers.IO).launch {
        val dataSnapshotList = functionA()
        functionB(dataSnapshotList)
    }
    

    请记住,最好使用以下方法将协程绑定到活动的生命周期:

    lifecycleScope.launch(Dispatchers.IO) {
        val dataSnapshotList = functionA()
        functionB(dataSnapshotList)
    }
    

    注意:

    这将基本上等待所有数据发生变化,以便触发onDataChanged(),当添加最后一个文件时,继续协程并返回值。根据用户的行为,这可能需要很长时间才能完成,因为即使其中一个文件没有更改,协程也不会恢复。

    另外,如果为一个文件触发了onCancelled(),这将永远不会完成。因此,如果您绝对确定所有文件都会触发onDataChanged(),请使用它。否则,实现某种超时功能以恢复不完整的数据。

    【讨论】:

    • 感谢您添加此内容。我不想依赖列表的大小,这不是固定的,也可以在两者之间进行更改。我正在等待所有响应的 android 中寻找类似的代码。 Pipelining requests
    猜你喜欢
    • 2018-12-14
    • 2019-05-30
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 2023-04-03
    • 2019-04-16
    相关资源
    最近更新 更多