如何解决Firebase Firestore查询无法正确返回用户名的问题
问题原因
Firestore 的 get() 是异步网络请求,调用后不会阻塞线程等待结果返回,会直接执行后续的return userName语句。此时addOnSuccessListener里的赋值逻辑还没触发,userName还是初始值null,所以函数永远返回null。等网络请求成功、你在日志里看到正确用户名的时候,getUserName函数早就执行完毕退出了,后续对userName的修改不会影响已经返回的结果。
解决方案
异步请求不能用同步return的方式返回值,以下是两种生产环境常用的实现方式:
方案1:回调函数接收结果(无协程依赖场景)
给函数增加回调参数,等请求成功拿到用户名后,通过回调把值传递出去:
fun getUserName(userID: String, onResult: (String?) -> Unit) { mFireStore.collection(Constants.USERS) .whereEqualTo(Constants.ID, userID) .get() .addOnSuccessListener { document -> var userName: String? = null for (i in document.documents) { val user = i.toObject(User::class.java)!! userName = user.userName Log.e("userName", userName!!) } // 拿到结果后通过回调传出 onResult(userName) } .addOnFailureListener { e -> Log.e("getUserName", "查询用户名失败", e) // 请求失败时回调传null onResult(null) } }
调用方式:
getUserName("目标用户ID") { name -> // 所有用到用户名的逻辑都要写在这个回调里 Log.d("查询结果", "用户名为:${name.orEmpty()}") }
方案2:协程挂起函数(项目接入Kotlin协程场景)
如果项目已经使用Kotlin协程,可以用挂起函数实现,写法更接近同步逻辑,避免回调嵌套:
// 注意这是挂起函数,只能在协程作用域或其他挂起函数里调用 suspend fun getUserName(userID: String): String? { return try { val document = mFireStore.collection(Constants.USERS) .whereEqualTo(Constants.ID, userID) .get() .await() // 协程扩展函数,会挂起等待请求返回结果,不阻塞主线程 var userName: String? = null for (i in document.documents) { val user = i.toObject(User::class.java)!! userName = user.userName } userName } catch (e: Exception) { Log.e("getUserName", "查询用户名失败", e) null } }
调用方式:
// 借助lifecycleScope/ViewModelScope等协程作用域调用 lifecycleScope.launch { val userName = getUserName("目标用户ID") // 可以直接拿到返回值做后续逻辑 }
优化提示:你当前的逻辑如果匹配到多个同ID用户,会取最后一个文档的用户名。正常来说UserID是用户唯一标识,建议直接用UserID作为USERS集合的文档ID,查询时直接调用
mFireStore.collection(Constants.USERS).document(userID).get()即可,查询效率更高,也不会出现多条重复结果的问题。
内容的提问来源于stack exchange,提问作者Jazim
相关产品推荐
相关产品推荐

