我正在为一个朋友开发一个应用程序,我使用Firestore.我想要的是显示一个最喜欢的地方的列表,但由于某种原因,列表总是空的.

enter image description here

我无法从Firestore获取数据.这是我的代码:

fun getListOfPlaces() : List<String> {
    val places = ArrayList<String>()
    placesRef.get().addOnCompleteListener { task ->
        if (task.isSuccessful) {
            for (document in task.result) {
                val name = document.data["name"].toString()
                places.add(name)
            }
        }
    }
    return list;
}

如果我试着打印,比如onCreate函数中列表的大小,大小总是0.

Log.d("TAG", getListOfPlaces().size().toString()); // Is 0 !!!

我可以确认Firebase已成功安装. 我遗漏了什么?

推荐答案

这是异步web API的classic 问题.您现在无法退回尚未加载的内容.换句话说,您不能简单地将places列表作为方法的结果返回,因为由于onComplete函数的异步行为,它将始终为空.根据您的连接速度和状态,数据可用可能需要几百毫秒到几秒钟.

But not only Cloud Firestore loads data asynchronously, almost all of modern other web APIs do, since it may take some time to get the data. But let's take an quick example, by placing a few log statements in the code, to see more clearly what I'm talking about.

fun getListOfPlaces() : List<String> {
    Log.d("TAG", "Before attaching the listener!");
    val places = ArrayList<String>()
    placesRef.get().addOnCompleteListener { task ->
        if (task.isSuccessful) {
            Log.d("TAG", "Inside onComplete function!");
            for (document in task.result) {
                val name = document.data["name"].toString()
                places.add(name)
            }
        }
    }
    Log.d("TAG", "连接听者之后!");
    return list;
}

If we run this code will, the output in your logcat will be:

Before attaching the listener!

连接听者之后!

Inside onComplete function!

This is probably not what you expected, but it explains precisely why your places list is empty when returning it.

对于大多数开发人员来说,最初的react 是try "修复"这个asynchronous behavior,我个人建议不要这么做."Here"是道格·史蒂文森写的一篇出色的文章,我强烈推荐你阅读.

这个问题的一个快速解决方法是只在onComplete函数中使用places list:

fun readData() {
    placesRef.get().addOnCompleteListener { task ->
        if (task.isSuccessful) {
            val list = ArrayList<String>()
            for (document in task.result) {
                val name = document.data["name"].toString()
                list.add(name)
            }
            //Do what you need to do with your list
        }
    }
}

如果你想在外面使用列表,还有另一种方法.您需要创建自己的回调,等待Firestore返回数据.要实现这一点,首先需要创建一个interface,如下所示:

interface MyCallback {
    fun onCallback(value: List<String>)
}

然后,您需要创建一个实际从数据库获取数据的函数.此方法应如下所示:

fun readData(myCallback : MyCallback) {
    placesRef.get().addOnCompleteListener { task ->
        if (task.isSuccessful) {
            val list = ArrayList<String>()
            for (document in task.result) {
                val name = document.data["name"].toString()
                list.add(name)
            }
            myCallback.onCallback(list)
        }
    }
}

See, we don't have any return type anymore. In the end just simply call readData() function in your onCreate function and pass an instance of the MyCallback interface as an argument like this:

readData(object: MyCallback {
    override fun onCallback(value: List<String>) {
        Log.d("TAG", list.size.toString())
    }
})

如果您正在使用Kotlin,请判断其他answer个.

Kotlin相关问答推荐

如何为集成测试配置Gradle JVM测试套件?

同时也是一个字符串的 Kotlin 枚举

垂直滚动条下拉菜单的桌面组合

如何将 `when` 与 2 个密封类一起使用并获取内部值?

使用 kotlin 流删除 map 中具有某些相似性的值

返回 kotlin 中的标签和 lambda 表达式

使用启动或使用 coroutineScope 启动协程之间的区别

Kotlin 中二叉树的深度

错误:cannot find symbol import com.gourav.news.databinding.ActivityDetailBindingImpl;

对列表中数字的子集求和

Foo::class.java 和 Foo::javaClass 有什么区别?

为什么 Kotlin Pair 中的条目不可变?

如何使用kotlin中的反射查找包中的所有类

面临一些未知问题一些后端jvm内部错误

Kotlin:如何修改成对的值?

使用导航组件在BottomNavigationView中传递参数

通过在 kotlin-gradle 中使用子项目Unresolved reference: implementation

Failure delivering result on activity result

Kotlin中的嵌套let块

我应该在哪里调用 MobileAds.initialize()?