如何在Kotlin中等待Firestore查询完成后再执行后续操作?
问题:Firestore异步查询导致返回空列表,如何用Kotlin协程正确处理?
我是Firestore和Kotlin的新手,写了个函数想检索指定日期范围内的文档ID并存入列表,但函数总是返回空值。我知道这是因为Firestore查询是异步的,还没等查询完成函数就返回了。加3秒延迟能临时解决,但这明显不是好办法,网络慢的时候肯定会出问题。尝试用协程解决,但搞不懂怎么用。
原函数代码:
fun documentsBetweenDates(date1 : String, date2 : String) : MutableList<String> { val db = FirebaseFirestore.getInstance() val retrievedDocuments = mutableListOf<String>() db.collection("users").document(getUID()) .collection("values") .whereGreaterThanOrEqualTo("purchaseDate", convertDateToMillis(date1)!!) .whereLessThanOrEqualTo("purchaseDate", convertDateToMillis(date2)!!) .get() .addOnSuccessListener { document -> for (documents in document) { retrievedDocuments.add(documents.id) Log.d("DB", "Document between dates found: ${retrievedDocuments}") } } return retrievedDocuments }
Fragment中的调用代码:
var test = dbController.documentsBetweenDates("22/02/2021", "22/02/2022") Log.d("DB", test.ToString())
解决方案:用Kotlin协程实现Firestore异步查询的同步调用
1. 原方法失效的核心原因
Firestore的get()是异步执行的,addOnSuccessListener内的逻辑要等查询完成后才会触发,但你的函数在调用get()后立刻返回了空的retrievedDocuments——这时候查询还在后台跑,列表根本没被填充。靠延迟“凑时间”完全不可靠,网络波动时必然出错。
2. 改造为协程挂起函数
Firebase官方支持Kotlin协程,只需把函数改成挂起函数,用await()替代回调,让协程自动等待查询完成:
// 新增suspend关键字,标记为协程挂起函数 suspend fun documentsBetweenDates(date1: String, date2: String): MutableList<String> { val db = FirebaseFirestore.getInstance() val retrievedDocuments = mutableListOf<String>() // 用await()替代addOnSuccessListener,协程会暂停直到查询返回结果 val querySnapshot = db.collection("users").document(getUID()) .collection("values") .whereGreaterThanOrEqualTo("purchaseDate", convertDateToMillis(date1)!!) .whereLessThanOrEqualTo("purchaseDate", convertDateToMillis(date2)!!) .get() .await() // 关键:阻塞协程直到查询完成 // 查询完成后再遍历填充列表 for (document in querySnapshot) { retrievedDocuments.add(document.id) Log.d("DB", "Document between dates found: ${retrievedDocuments}") } return retrievedDocuments }
3. 在Fragment中正确调用挂起函数
挂起函数不能直接在主线程同步调用,需要借助协程作用域启动协程。推荐用Fragment自带的lifecycleScope(需依赖AndroidX Lifecycle库),它会自动和Fragment生命周期绑定,避免内存泄漏:
// 比如在Fragment的onViewCreated方法中调用 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) lifecycleScope.launch { val test = dbController.documentsBetweenDates("22/02/2021", "22/02/2022") Log.d("DB", test.toString()) // 在这里处理获取到的列表,比如更新UI } }
4. 必加的异常处理
Firestore查询可能因网络错误、权限不足等原因失败,建议用try-catch捕获异常:
suspend fun documentsBetweenDates(date1: String, date2: String): MutableList<String> { val db = FirebaseFirestore.getInstance() val retrievedDocuments = mutableListOf<String>() try { val querySnapshot = db.collection("users").document(getUID()) .collection("values") .whereGreaterThanOrEqualTo("purchaseDate", convertDateToMillis(date1)!!) .whereLessThanOrEqualTo("purchaseDate", convertDateToMillis(date2)!!) .get() .await() for (document in querySnapshot) { retrievedDocuments.add(document.id) Log.d("DB", "Document between dates found: ${retrievedDocuments}") } } catch (e: Exception) { Log.e("DB", "查询失败: ${e.message}") // 可根据需求返回空列表、抛出异常或提示用户 } return retrievedDocuments }
内容的提问来源于stack exchange,提问作者Noel Pettersson
相关产品推荐
相关产品推荐

