SqlDelight Coroutines Flow无法向Jetpack Glance Widget发射数据
待办Widget数据更新不触发Flow问题排查与修复
问题背景
基于Jetpack Glance开发待办Widget,使用SqlDelight做数据持久化。首次加载数据正常,调试日志能打印;但在provideGlance运行期间向数据库写入新数据时,Flow无新事件触发,Widget也不会重组更新。
相关代码
GlanceWidget实现
override suspend fun provideGlance(context: Context, id: GlanceId) { val pageRepository = EntryPoints.get(context, PageProviderEntryPoint::class.java).pageRepository() val initialPages = pageRepository.getSortedPages(config!!.databaseId, null) provideContent { val pages by pageRepository.getSortedPagesFlow(config.databaseId, null) .collectAsState(initial = initialPages) GlanceTheme { CheckListLayout(pages) } } }
PageRepository实现
fun getSortedPagesFlow( databaseId: String, filter: Filter.CompanionFilter? ) = localDatabase.getSortedPagesFlow(databaseId, filter).onEach { println("debug pages count ${it.size}") } fun getSortedPages( databaseId: String, filter: Filter.CompanionFilter? ) = localDatabase.getSortedPages(databaseId, filter)
LocalDatabase实现
override fun getSortedPages( databaseId: String, filter: Filter.CompanionFilter? ): List<Page> { return dbQuery.selectAllPages(databaseId, ::mapToPage) .executeAsList() .filter { filter?.evaluate(it) ?: true } .sortedBy { Instant.parse(it.lastEditedTime) }.reversed() } override fun getSortedPagesFlow( databaseId: String, filter: Filter.CompanionFilter? ): Flow<List<Page>> { return dbQuery.selectAllPages(databaseId, ::mapToPage) .asFlow() .mapToList(Dispatchers.Main) .onEach { println("debug pages count localdb ${it.size}") } .map { pages -> pages.filter { filter?.evaluate(it) ?: true }} .map { pages -> pages.sortedBy { Instant.parse(it.lastEditedTime) }.reversed()} }
问题分析与修复方案
1. 检查SqlDelight查询的监听有效性
SqlDelight的asFlow()仅在查询结果关联的表发生数据变更时触发事件,需确保selectAllPages的SQL查询定义正确,能覆盖目标表的所有变更场景。例如:
selectAllPages: SELECT * FROM page WHERE database_id = ?;
SqlDelight会自动为这类查询生成带表变更监听的Query实例,确保插入/更新/删除操作能触发Flow事件。
2. 修正Flow的线程调度
数据库操作属于IO任务,mapToList应使用Dispatchers.IO而非Main,避免阻塞主线程同时确保Flow能正确接收数据库变更:
override fun getSortedPagesFlow( databaseId: String, filter: Filter.CompanionFilter? ): Flow<List<Page>> { return dbQuery.selectAllPages(databaseId, ::mapToPage) .asFlow() .mapToList(Dispatchers.IO) .onEach { println("debug pages count localdb ${it.size}") } .map { pages -> pages.filter { filter?.evaluate(it) ?: true }} .map { pages -> pages.sortedBy { Instant.parse(it.lastEditedTime) }.reversed() } .flowOn(Dispatchers.IO) }
3. 优化Glance中的Flow收集逻辑
无需提前获取初始值,collectAsState可自动处理初始状态,同时确保在Glance的Composition上下文内正确收集Flow:
override suspend fun provideGlance(context: Context, id: GlanceId) { val pageRepository = EntryPoints.get(context, PageProviderEntryPoint::class.java).pageRepository() val databaseId = config!!.databaseId provideContent { val pages by pageRepository.getSortedPagesFlow(databaseId, null) .collectAsState(initial = emptyList()) GlanceTheme { CheckListLayout(pages) } } }
4. 兜底:手动触发Widget更新
若上述调整后仍无效果,可在数据库写入操作完成后,手动调用Glance的更新接口强制刷新Widget:
// 数据写入完成后执行 GlanceWidgetManager(context).updateGlanceWidget(id)
5. 检查SqlDelight版本
确保使用最新稳定版SqlDelight(如2.0.x系列),旧版本可能存在Flow监听的兼容性bug。
内容的提问来源于stack exchange,提问作者Micha
相关产品推荐
相关产品推荐

