You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firestore Kotlin创建坐标嵌套数组遇空值问题求助

解决Firestore异步回调导致外部数组为空的问题

兄弟,你这是踩中了异步操作的经典陷阱!我来给你掰扯清楚:

Firestore的get()方法是异步执行的——它不会卡在那里等服务器返回数据,而是直接继续往下跑后面的代码。所以你的代码执行顺序其实是这样的:

  1. 发起Firestore的获取请求
  2. 立刻执行监听器外面的println,这时候posiciones还没被填充,自然是空数组
  3. 等服务器把数据传回来后,才会触发addOnSuccessListener里的循环,把数据加到数组里

说白了就是:你打印的时机太早了,数组还没来得及被赋值呢!

给你三种常见的解决方案:

方案1:把逻辑挪到回调内部(最简单直接)

既然数据只有在回调里才准备好,那所有需要用这个数组的代码都放到addOnSuccessListener里面就行:

myDB.collection("players")
    .get()
    .addOnSuccessListener { result ->
        for (document in result) {
            posiciones.add(
                arrayOf(
                    document.get("posx").toString(),
                    document.get("posy").toString()
                )
            )
        }
        // 在这里使用数组,打印或者传给其他函数都没问题
        println("Testing: " + posiciones)
        updateUIWithPositions(posiciones) // 比如更新UI的逻辑
    }
    .addOnFailureListener { exception ->
        Log.d(ContentValues.TAG, "Error getting documents: ", exception)
    }

方案2:用Kotlin协程让代码“看起来同步”(更优雅)

如果不想把代码都塞在回调里,用协程可以让异步代码写得像同步一样。首先确保你加了协程依赖(在build.gradle里):

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3" // 版本可以用最新的

然后在Activity里用lifecycleScope(避免内存泄漏)来执行:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    lifecycleScope.launch {
        try {
            // 用await()等待Firestore返回结果
            val result = myDB.collection("players").get().await()
            for (document in result) {
                posiciones.add(
                    arrayOf(
                        document.get("posx").toString(),
                        document.get("posy").toString()
                    )
                )
            }
            // 这里可以安全使用数组了
            println("Testing: " + posiciones)
        } catch (exception: Exception) {
            Log.d(ContentValues.TAG, "Error getting documents: ", exception)
        }
    }
}

方案3:自定义回调接口(适合复用场景)

如果这个获取位置的逻辑要在多个地方用,可以封装成带回调的函数:
先定义一个回调接口:

interface PositionsCallback {
    fun onSuccess(positions: ArrayList<Array<String>>)
    fun onFailure(error: Exception)
}

然后写获取数据的函数:

fun fetchPlayerPositions(callback: PositionsCallback) {
    myDB.collection("players")
        .get()
        .addOnSuccessListener { result ->
            val positions = ArrayList<Array<String>>()
            for (document in result) {
                positions.add(
                    arrayOf(
                        document.get("posx").toString(),
                        document.get("posy").toString()
                    )
                )
            }
            callback.onSuccess(positions)
        }
        .addOnFailureListener { exception ->
            callback.onFailure(exception)
        }
}

最后在onCreate里调用:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    fetchPlayerPositions(object : PositionsCallback {
        override fun onSuccess(positions: ArrayList<Array<String>>) {
            println("Testing: " + positions)
            // 在这里处理数据
        }

        override fun onFailure(error: Exception) {
            Log.d(ContentValues.TAG, "Error getting documents: ", error)
        }
    })
}

总结一下

核心就是记住:Firestore的所有网络请求都是异步的,依赖返回数据的逻辑必须等数据回来后再执行——要么塞在回调里,要么用协程/自定义回调来处理,绝对不能在请求外面直接同步使用还没被赋值的变量!

内容的提问来源于stack exchange,提问作者Puntogris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:06:28