Kotlin Flow在Composable中未收集,GraphQL查询无法运行
问题排查与修复方案
核心问题推测
你的webPaymentSettings始终停留在NotStarted,大概率是因为流未发射任何值,导致collectAsStateWithLifecycle一直使用传入的初始值。最可能的诱因是queryFactory中的阻塞挂起,或是流构造逻辑存在缺陷。
分步排查步骤
1. 加日志追踪流的完整生命周期
在关键节点添加日志,确认流是否正常发射、Query是否创建、响应是否接收:
// 在GraphQuery的flow中添加日志 public val flow = control .onStart { Log.d("GraphQueryDebug", "Flow started, emitting Loading") emit(GraphQueryState.Loading()) } .flatMapConcat { state -> Log.d("GraphQueryDebug", "Processing control state: $state") runCatching { queryFactory() }.fold( onSuccess = { query -> Log.d("GraphQueryDebug", "Successfully created query: $query") api.client .query(query) .fetchPolicy(FetchPolicy.NetworkOnly) .watch() .onStart { Log.d("GraphQueryDebug", "GraphQL watch flow started") } .map { response -> Log.d("GraphQueryDebug", "Received response: hasErrors=${response.hasErrors()}") when (response.hasErrors()) { true -> GraphQueryState.Error(response.errors!!) else -> GraphQueryState.Ready(response.data!!) } } }, onFailure = { e -> Log.e("GraphQueryDebug", "Failed to create query", e) flowOf(GraphQueryState.Error(listOf(GqlError(message = e.localizedMessage)))) } ) } // 在ViewModel的settings流中添加日志 public val settings = settingsQuery.flow.onEach { Log.d("GraphQueryDebug", "Emitted final state: $it") } // 在Composable中手动验证订阅 @Composable fun EditWebPaymentSettingsScreen(...) { // ... LaunchedEffect(Unit) { viewModel.settings.collect { state -> Log.d("ComposableDebug", "Collected state: $state") } } }
运行后查看Logcat,重点关注:
- 是否有
Flow started日志(确认流被订阅) - 是否有
Successfully created query日志(确认Query未被阻塞) - 是否有
Received response日志(确认GraphQL请求有返回)
2. 修复queryFactory的阻塞问题
你的queryFactory中调用了appSettings.data.last(),而DataStore的data流是永不终止的热流,last()会一直挂起等待流结束,导致Query无法创建,整个流彻底卡住。
替换为first()获取第一个可用的配置值:
private val settingsQuery = GraphQuery(api) { // 用first()获取第一个加载完成的配置,而非last()等待流结束 val settings = appSettings.data.first() // 可选:添加过滤确保有有效组织ID // val settings = appSettings.data.first { !it.currentOrganizationId.isNullOrEmpty() } SettingsForAdminQuery( UUID.fromString(settings.currentOrganizationId), SupportedSettingPurpose.WebPayment.value, ) }
如果需要监听配置变化并自动刷新查询,应该将配置流与查询流结合,而非在queryFactory中阻塞获取。
3. 验证GraphQL客户端的watch()行为
部分GraphQL客户端的watch()方法仅在数据变化时发射值,初始查询不会主动返回结果。可以改为先执行单次fetch()获取初始数据,再监听后续变化:
api.client .query(query) .fetchPolicy(FetchPolicy.NetworkOnly) .fetch() // 先获取单次初始结果 .flatMapConcat { initialResponse -> flowOf(initialResponse) + api.client.query(query).watch() // 再监听后续更新 } .map { response -> // 状态转换逻辑不变 }
优化后的GraphQuery实现
针对重复查询、状态冗余问题,调整流逻辑:
class GraphQuery<DataType : Query.Data>( private val api: Api, private val queryFactory: suspend () -> Query<DataType>, ) { private val control: MutableStateFlow<Unit> = MutableStateFlow(Unit) public val flow = control .onStart { emit(Unit) } .distinctUntilChanged() .flatMapLatest { // 取消旧查询,避免并发请求 flow { emit(GraphQueryState.Loading()) runCatching { queryFactory() }.fold( onSuccess = { query -> api.client .query(query) .fetchPolicy(FetchPolicy.NetworkOnly) .watch() .collect { response -> emit( if (response.hasErrors()) { GraphQueryState.Error(response.errors!!) } else { GraphQueryState.Ready(response.data!!) } ) } }, onFailure = { e -> emit(GraphQueryState.Error(listOf(GqlError(message = e.localizedMessage)))) } ) } } public fun refresh() { control.value = Unit } }
内容的提问来源于stack exchange,提问作者Alexander Trauzzi
相关产品推荐
相关产品推荐

