【问题标题】:How to wait for a function to complete in android (kotlin)?如何在 android (kotlin) 中等待函数完成?
【发布时间】:2022-11-11 09:57:43
【问题描述】:

我是android开发的初学者。我正在尝试实现回收器视图,该视图在从 firebase 数据库下载后显示组列表。 从主活动调用函数 loadGroups() 以返回一个列表,然后将其馈送到回收器视图适配器。 数据正在正确下载,但似乎首先返回 myList ,然后在几毫秒后添加来自 firebase 的元素。我希望程序等待元素被添加到 myList 然后返回它

class DataSource {
private lateinit var myDatabase: DatabaseReference
var myList : MutableList<Group> = mutableListOf<Group>();

fun loadGroups(): MutableList<Group> {
    // Here I want to let the loadGroupsFromFirebase() complete adding groups to mylist 
    // after that is completed, mylist should be returned
    loadGroupsFromFirebase()
    
    Log.d("mylist", "returning my list")
    return myList
}

private fun loadGroupsFromFirebase(){
    myDatabase = FirebaseDatabase.getInstance().getReference("myGroupsList")

    val postListener = object : ValueEventListener {
        override fun onDataChange(myDataSnapshot: DataSnapshot) {
            if(myDataSnapshot.exists()){
                Log.d("mylist", "does exist ${myDataSnapshot.getValue().toString()}")
                myList.clear()

                for(dataSnapshot in myDataSnapshot.children){
                    val myGroupDetails = dataSnapshot.getValue<Group>()!!;
                    myList.add(myGroupDetails)
                    myList.add(Group(myIconId=2131165282, myTitle="G1", myLink = "https://s*****************************************9", numberOfPeople=100))
                    Log.d("mylist", "does exist CODE 00 ${myList.toString()}")
                }
            }
            else {
                Log.d("mylist", "does not exist")
            }
        }

        override fun onCancelled(databaseError: DatabaseError) {
            // Getting Post failed, log a message
            Log.w("mylist", "loadPost:onCancelled", databaseError.toException())
        }
    }
    myDatabase.addValueEventListener(postListener)
}

}

任何帮助,将不胜感激 :) 下面是logcat的截图。

【问题讨论】:

    标签: android firebase kotlin android-recyclerview kotlin-coroutines


    【解决方案1】:

    值事件监听器是一个接口,因此在 onDataChange 上运行的所有内容都是一个异步操作(因为取决于网络响应),另一方面负载组()是一个同步操作,所以loadGroupsFromFirebase()是第一次和立即调用返回我的列表被称为,但是loadGroupsFromFirebase()还没有完成。

    所以你需要在for 循环

        for(dataSnapshot in myDataSnapshot.children){
                            val myGroupDetails = dataSnapshot.getValue<Group>()!!;
                            myList.add(myGroupDetails)
                            myList.add(Group(myIconId=2131165282, myTitle="G1", myLink = "https://s*****************************************9", numberOfPeople=100))
                            Log.d("mylist", "does exist CODE 00 ${myList.toString()}")
                        }
    
    //-- HERE, TAKE myList and save it somehow.
    

    或者,实现 MVP 或 MVVM 模式,这样您就可以正确处理异步响应。

    【讨论】:

    • 感谢您的回复,但 myList 必须在调用函数 loadGroups() 时在另一个活动中返回
    【解决方案2】:

    修改你的函数loadGroupsFromFirebase()有一个单位返回

    private fun loadGroupsFromFirebase(result: (List<Group>) -> Unit){
    myDatabase = FirebaseDatabase.getInstance().getReference("myGroupsList")
    
    val postListener = object : ValueEventListener {
        override fun onDataChange(myDataSnapshot: DataSnapshot) {
            if(myDataSnapshot.exists()){
                Log.d("mylist", "does exist ${myDataSnapshot.getValue().toString()}")
                myList.clear()
    
                for(dataSnapshot in myDataSnapshot.children){
                    val myGroupDetails = dataSnapshot.getValue<Group>()!!;
                    myList.add(myGroupDetails)
                    myList.add(Group(myIconId=2131165282, myTitle="G1", myLink = "https://s*****************************************9", numberOfPeople=100))
                    Log.d("mylist", "does exist CODE 00 ${myList.toString()}")
                }
            }
            else {
                Log.d("mylist", "does not exist")
            }
            result(myList)
        }
    
        override fun onCancelled(databaseError: DatabaseError) {
            // Getting Post failed, log a message
            Log.w("mylist", "loadPost:onCancelled", databaseError.toException())
        }
    }
    myDatabase.addValueEventListener(postListener)
    

    }

    【讨论】:

    • 收到错误:“没有为参数结果传递值”
    【解决方案3】:

    由于 Firebase API 是异步的,因此您无法同步返回 myList 作为方法的结果。解决方案始终相同,任何需要来自实时数据库的数据的代码都需要在 onDataChange() 方法中,或者从那里调用。

    由于您使用的是 Kotlin,请注意我们有一种更简单的方法来解决异步调用。所以我会告诉你如何使用Kotlin Coroutines

    为了让它工作,我们需要在 build.gradle 文件中添加以下依赖项:

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

    我们使用的这个库称为Module kotlinx-coroutines-play-services。正如我之前提到的,我们无法将对象列表作为方法的结果返回,因为get() 会立即返回,而它返回的 Task 的回调将在稍后调用。这就是为什么我们应该等到数据可用的原因。

    在返回的Task 对象上调用“get()”时,我们可以附加一个侦听器,以便获得查询结果。我们现在需要做的是将其转换为与 Kotlin Coroutines 一起使用的东西。为此,我们需要创建一个如下所示的挂起函数:

    private suspend fun getGroupList(): List<DocumentSnapshot> {
        val snapshot = myDatabase.get().await()
        return snapshot.documents
    }
    

    如您所见,我们现在有一个名为await() 的扩展函数,它将中断协程,直到实时数据库中的数据可用,然后返回。现在我们可以简单地从另一个挂起方法调用它,如以下代码行:

    private suspend fun getGroupListFromFirestore() {
        try {
            val groupList = getGroupList()
        } catch (e: Exception) {
            Log.d(TAG, e.getMessage()) //Don't ignore potential errors!
        }
    }
    

    差不多就是这样。如果你有兴趣,我还写了一篇文章,叫做:

    这将帮助您更好地理解这个概念。

    【讨论】:

      猜你喜欢
      • 2021-10-15
      • 1970-01-01
      • 2019-11-07
      • 1970-01-01
      • 2023-01-31
      • 1970-01-01
      • 1970-01-01
      • 2020-12-16
      • 1970-01-01
      相关资源
      最近更新 更多