Firebase 实时数据库等待数据被检索

Firebase Realtime Database wait until the data is retrieved

我有一个包含我的 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 {} 但没有成功。

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

如果您需要 , then you should consider using kotlinx-coroutines-play-services, case in which you can use awaitAll() 功能。

这是一种方法:

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)
}

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

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

注:

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

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

你可以通过使用任务来实现它。 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() 
        }
    }
}