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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 22:42:08