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

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时提供无参构造函数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 17:44:49