Firestore查询无结果:Jetpack Compose Android应用技术排查请求
Firestore查询无结果且无报错问题排查与解决方案
问题描述
基于Make it so Android示例项目,使用Jetpack Compose + Firebase开发Android应用时,查询Firestore中名为courses的集合(该集合包含1个文档),程序运行后既无结果显示,也未出现任何报错信息。
相关信息
Firestore数据与安全规则说明
- 数据:
courses集合内存在1个有效文档 - 安全规则:
courses集合的读写权限限制为request.auth != null,即要求用户必须登录才能访问
核心代码
1. CoursesServiceImpl.kt
package com.tnmd.learningenglishapp.model.service.imple class CoursesServiceImpl @Inject constructor(private val firestore: FirebaseFirestore, private val auth: AccountService ) : CoursesService { override val courses: Flow<List<Courses>> // 查询逻辑所在 get() = firestore.collection(COURSES_COLLECTION).dataObjects() override suspend fun getCourses(coursesId: String): Courses? = firestore.collection(COURSES_COLLECTION).document(coursesId).get().await().toObject() override suspend fun save(courses: Courses): String = trace(SAVE_COURSES_TRACE){ firestore.collection(COURSES_COLLECTION).add(courses).await().id } override suspend fun update(courses: Courses): Unit = trace(UPDATE_COURES_TRACE){ firestore.collection(COURSES_COLLECTION).document(courses.id).set(courses).await() } override suspend fun delete(coursesId: String) { firestore.collection(COURSES_COLLECTION).document(coursesId).delete().await() } companion object { private const val USER_ID_FIELD = "userId" private const val COURSES_COLLECTION = "courses" private const val SAVE_COURSES_TRACE = "saveCourses" private const val UPDATE_COURES_TRACE = "updateCourses" } }
2. CoursesViewModel.kt
package com.tnmd.learningenglishapp.screens.list_courses @HiltViewModel class CoursesViewModel @Inject constructor( private val coursesService: CoursesService, logService: LogService ) : LearningEnglishAppViewModel(logService){ val courses = coursesService.courses }
3. CoursesScreen.kt
package com.tnmd.learningenglishapp.screens.list_courses @SuppressLint("UnusedMaterialScaffoldPaddingParameter") @Composable @ExperimentalMaterialApi fun CoursesScreen( modifier: Modifier = Modifier, viewModel: CoursesViewModel = hiltViewModel() ) { Scaffold( floatingActionButton = { FloatingActionButton( onClick = { /*viewModel.onUpdateClick(openScreen)*/ }, backgroundColor = MaterialTheme.colors.primary, contentColor = MaterialTheme.colors.onPrimary, modifier = modifier.padding(16.dp) ) { Icon(Icons.Filled.Add, "Update") } } ) { val courses = viewModel.courses.collectAsStateWithLifecycle(emptyList()) Column(modifier = Modifier .fillMaxWidth() .fillMaxHeight()) { Text(text = courses.value.size.toString(), color = Color.DarkGray) LazyColumn { items(courses.value, key = { it.id }) { coursesItem -> CoursesItem( courses = coursesItem ) } } } } }
排查方向与解决方案
1. 安全规则权限限制
根据规则配置,courses集合要求用户必须登录才能访问。如果应用未完成用户登录流程,Firestore会静默拒绝查询请求,不会抛出异常。
解决办法:
- 确认应用已实现Firebase Auth登录逻辑,确保查询Firestore前用户已完成登录
- 测试阶段可临时修改安全规则(上线前需恢复):
match /courses/{course} { allow read, write: if true; }
2. 自定义dataObjects()扩展函数异常
firestore.collection(...).dataObjects()是自定义扩展函数,可能存在数据转换错误或异常吞掉的情况。
解决办法:
替换为标准Flow实现测试,验证是否能获取数据:
override val courses: Flow<List<Courses>> get() = callbackFlow { val listener = firestore.collection(COURSES_COLLECTION) .addSnapshotListener { snapshot, error -> if (error != null) { trySend(emptyList()) close(error) return@addSnapshotListener } val coursesList = snapshot?.documents?.mapNotNull { it.toObject(Courses::class.java) } ?: emptyList() trySend(coursesList) } awaitClose { listener.remove() } }
3. Courses数据类序列化问题
如果Courses类的字段与Firestore文档字段不匹配(名称大小写、数据类型不一致),toObject()会返回null,导致列表为空。
解决办法:
- 检查
Courses数据类字段与Firestore文档字段完全一致 - 确保
Courses类添加正确的序列化注解:- 使用Kotlinx序列化时添加
@Serializable - 使用Gson时提供无参构造函数
- 使用Kotlinx序列化时添加
4. 日志缺失导致无法排查异常
当前代码未对Firestore查询异常做日志记录,无法定位潜在错误。
解决办法:
在CoursesServiceImpl中添加异常捕获与日志:
override val courses: Flow<List<Courses>> get() = firestore.collection(COURSES_COLLECTION).dataObjects() .catch { exception -> logService.logNonFatalCrash(exception) emit(emptyList()) }
5. Flow收集与生命周期问题
虽然使用了collectAsStateWithLifecycle,但需确认Flow是否正常发射数据。
解决办法:
在CoursesViewModel中添加日志,观察数据发射情况:
val courses = coursesService.courses .onEach { Log.d("CoursesViewModel", "当前课程列表数量: ${it.size}") } .shareIn(viewModelScope, SharingStarted.WhileSubscribed(5000), replay = 1)
内容的提问来源于stack exchange,提问作者TranDatK
相关产品推荐
相关产品推荐

