This is a classic issue with asynchronous web APIs.
(这是异步Web API的经典问题。)
You cannot return something now, that hasn't been loaded yet. (您现在无法返回尚未加载的内容。)
With other words, you cannot simply return the places
list as a result of a method because it will always be empty due the asynchronous behavior of the onComplete
function. (换句话说,您不能简单places
由于方法的结果而返回places
列表,因为由于onComplete
函数的异步行为,该列表将始终为空。)
Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available. (根据您的连接速度和状态,可能需要数百毫秒到几秒钟的时间才能获得该数据。)
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.
(但是,不仅Cloud Firestore异步加载数据,几乎所有其他现代Web API也会异步加载数据,因为获取数据可能需要一些时间。)
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", "After attaching the listener!");
return list;
}
If we run this code will, the output in your logcat will be:
(如果我们运行此代码,您的logcat中的输出将是:)
Before attaching the listener!
(连接听众之前!)
After attaching the listener!
(附加监听器后!)
Inside onComplete function!
(里面的onComplete函数!)
This is probably not what you expected, but it explains precisely why your places list is empty when returning it.
(这可能不是您所期望的,但恰恰说明了为什么返回时您的地点列表为空。)
The initial response for most developers is to try and "fix" this asynchronous behavior
, which I personally recommend against it.
(对于大多数开发人员而言,最初的响应是尝试并“修复”这种asynchronous behavior
,我个人对此建议这样做。)
Here is an excelent article written by Doug Stevenson that I'll highly recommend you to read. (这是道格·史蒂文森(Doug Stevenson)撰写的精彩文章,我强烈建议您阅读。)
A quick solve for this problem would be to use the places list only inside the onComplete
function:
(快速解决此问题的方法是仅在onComplete
函数内部使用场所列表:)
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
}
}
}
If you want to use the list outside, there is another approach.
(如果要在外面使用列表,则有另一种方法。)
You need to create your own callback to wait for Firestore to return you the data. (您需要创建自己的回调以等待Firestore向您返回数据。)
To achieve this, first you need to create an interface
like this: (为此,首先需要创建一个如下interface
:)
interface MyCallback {
fun onCallback(value: List<String>)
}
Then you need to create a function that is actually getting the data from the database.
(然后,您需要创建一个实际上从数据库获取数据的函数。)
This method should look like this: (此方法应如下所示:)
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: (最后,只需在onCreate
函数中调用readData()
函数,然后将MyCallback
接口的实例作为参数传递如下:)
readData(object: MyCallback {
override fun onCallback(value: List<String>) {
Log.d("TAG", list.size.toString())
}
})