Kotlin中Firebase的addOnSuccessListener返回值滞后无法正确判断用户是否已提交餐厅点评
问题根源
Firebase Firestore 的查询属于异步非阻塞操作,addOnSuccessListener 是查询完成后的回调函数,不会阻塞主线程执行。你调用 fetchIfDataExist 后,主线程不会等待查询结束,会直接执行后续的 Log 打印和 if 判断逻辑,此时回调还没触发,existingReview 自然还是初始值 false。
解决方案
有两种常用的实现方式,你可以根据项目技术栈选择:
方案1:接口回调(无额外依赖,适配所有项目)
步骤1:定义回调接口
interface OnReviewCheckCallback { fun onCheckResult(isExisting: Boolean) }
步骤2:改造 fetchIfDataExist 函数
移除全局变量 existingReview,将回调作为入参,查询完成后通过回调返回结果:
private fun fetchIfDataExist(restaurant: String, callback: OnReviewCheckCallback) { Log.d("URL_DEBUG", "restaurant name: $restaurant") var isExisting = false FirebaseFirestore.getInstance().collection("user_review") .get() .addOnSuccessListener { result -> loop@ for (document in result) { if (document.id.contains(email)) { val user = document.toObject(User_Review::class.java) if (user.restaurant == restaurant) { isExisting = true break@loop } } } callback.onCheckResult(isExisting) } .addOnFailureListener { Toast.makeText(this, "查询点评记录失败", Toast.LENGTH_LONG).show() // 失败时可按业务需求返回默认值,这里默认返回未提交 callback.onCheckResult(false) } }
步骤3:调整调用逻辑
把原来后续的判断逻辑全部移到回调实现中:
if (email == "guest_email") { showAlertAuth() } else { fetchIfDataExist(placeName, object : OnReviewCheckCallback { override fun onCheckResult(isExisting: Boolean) { Log.d("URL_DEBUG", "fuera: $isExisting") if (isExisting) { showAlertExistingReview() } else { val intent = Intent(this@你当前的Activity类名, ReviewActivity::class.java).apply { putExtra("email", email) putExtra("restaurant", placeName) putExtra("edit", false) } startActivity(intent) } } }) }
注意把代码里的你当前的Activity类名替换为你实际的Activity类名。
方案2:Kotlin 协程(代码更简洁,适合已接入协程的项目)
步骤1:引入 Firestore Ktx 扩展
在模块级 build.gradle 中添加依赖:
implementation "com.google.firebase:firebase-firestore-ktx:24.10.0" // 可替换为最新版本
步骤2:将 fetchIfDataExist 改造为挂起函数
private suspend fun fetchIfDataExist(restaurant: String): Boolean { Log.d("URL_DEBUG", "restaurant name: $restaurant") return try { val result = FirebaseFirestore.getInstance().collection("user_review").get().await() // 直接匹配符合条件的记录 result.any { document -> document.id.contains(email) && document.toObject(User_Review::class.java).restaurant == restaurant } } catch (e: Exception) { Toast.makeText(this, "查询点评记录失败", Toast.LENGTH_LONG).show() false } }
步骤3:协程中调用函数
if (email == "guest_email") { showAlertAuth() } else { lifecycleScope.launch { val existingReview = fetchIfDataExist(placeName) Log.d("URL_DEBUG", "fuera: $existingReview") if (existingReview) { showAlertExistingReview() } else { val intent = Intent(this@你当前的Activity类名, ReviewActivity::class.java).apply { putExtra("email", email) putExtra("restaurant", placeName) putExtra("edit", false) } startActivity(intent) } } }
性能优化建议
你当前的实现会拉取user_review集合的全量数据再过滤,数据量增大后性能会很差,建议直接使用Firestore的条件查询提前过滤,避免全表扫描:
// 示例:直接查询对应用户+对应餐厅的记录,不需要拉取全量数据 FirebaseFirestore.getInstance().collection("user_review") .whereEqualTo("restaurant", restaurant) // 如果你的文档ID直接是用户邮箱,可以再加条件.whereEqualTo(FieldPath.documentId(), email),性能提升更明显 .get()
内容的提问来源于stack exchange,提问作者Hilcar
相关产品推荐
相关产品推荐

