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

SharedFlow结合使用时偶发未触发,旧过滤数据异常展示问题

问题描述

在另一个Fragment中修改过滤条件,返回后过滤条件已保存,但SharedFlow时常未触发,导致展示的数据不正确。正常运行3-4次后,就会一直展示旧过滤条件对应的旧数据。

相关代码

ViewModel 代码

lateinit var archivedSettingsFlow: SharedFlow<ArchivedSettings>
[...]
private fun createArchivedSettingsFlow() {
    val typesFlow = filtersRepository.getActiveFilters(FiltersType.ArchivedWorkItems.type)
    val sortByFlow = filtersRepository.getActiveFilter(FiltersType.ArchivedWorkItems.sortBy)
    val directionFlow =
        filtersRepository.getActiveFilter(FiltersType.ArchivedWorkItems.direction)
    val viewSettingsFlow = settingsRepository.getSettings(SettingsType.ArchivedWorkItems)

    val defaults =
        FiltersType.ArchivedWorkItems.defaultFilters().associateBy { filter -> filter.key }
    archivedSettingsFlow = combine(
        typesFlow,
        sortByFlow,
        directionFlow,
        viewSettingsFlow
    ) { types, sortBy, direction, viewSettings ->
        if (params.archivedMode) {
            _paramsFlow.update { params -> params.copy(currentPage = 0, hasNextPage = true) }
        }

        ArchivedSettings(
            types,
            sortBy ?: defaults.getValue(FiltersType.ArchivedWorkItems.sortBy.key),
            direction ?: defaults.getValue(FiltersType.ArchivedWorkItems.direction.key),
            viewSettings
        )
    }.debounce(250L)
        .shareIn(viewModelScope, SharingStarted.Lazily, replay = 1)
}

Fragment 的 onViewCreated 代码

lifecycleScope.launchWhenResumed {
    viewModel.archivedSettingsFlow.filter { it != null }.collectLatest {
        onScrollListener?.reset()
        adapter.submitList(emptyList())
        viewModel.fetch()
    }
}

LogCat 时序(编辑1)

2022-11-21 12:54:07.790 -> 首次发射旧值
2022-11-21 12:54:08.725 -> 第二次发射正确新值

多次操作后,只会触发首次发射的旧值

问题分析与解决方案

核心原因排查

  1. SharingStarted.Lazily 特性限制:该策略会在第一个订阅者出现时启动流,所有订阅者取消后流会停止。Fragment从返回栈恢复时,重新订阅只会重播最后一次缓存值,若上游的过滤条件流未发射新值,combine不会重新计算新的ArchivedSettings。
  2. 上游流可能是冷流:如果filtersRepository返回的是冷流(每次订阅重新获取数据而非持续监听变化),修改过滤条件后不会主动触发流的新值发射,导致combine无新数据可计算。
  3. debounce 可能吞掉事件:若过滤条件修改操作的间隔小于250ms,debounce会丢弃中间事件;若上游流未正确发射新值,该操作会直接导致无事件触发。

针对性解决方案

1. 更换流的启动策略

将SharingStarted.Lazily改为SharingStarted.WhileSubscribed(5000L),让流在最后一个订阅者取消后仍保持活跃5秒,避免频繁重启流导致的状态丢失,同时确保上游流持续监听变化:

archivedSettingsFlow = combine(/* ... */) { /* ... */ }
    .debounce(250L)
    .shareIn(viewModelScope, SharingStarted.WhileSubscribed(5000L), replay = 1)

2. 确保上游流为热流(如StateFlow)

检查filtersRepository中的过滤条件存储逻辑,将其改为用StateFlow维护状态,确保修改过滤条件时主动发射新值:

// 示例:Repository中用StateFlow维护过滤条件状态
private val _activeSortBy = MutableStateFlow<Filter?>(null)
val activeSortByFlow: StateFlow<Filter?> = _activeSortBy

fun updateActiveSortBy(filter: Filter) {
    _activeSortBy.value = filter
}

3. 移除无意义的非空过滤

ArchivedSettings构造时均为非空值,filter { it != null }属于冗余操作,可能引发意外问题,建议直接移除:

lifecycleScope.launchWhenResumed {
    viewModel.archivedSettingsFlow.collectLatest {
        onScrollListener?.reset()
        adapter.submitList(emptyList())
        viewModel.fetch()
    }
}

4. 让params状态变化触发流更新

若params是可变状态,将其转为StateFlow并加入combine的流列表,确保archivedMode变化时也能触发ArchivedSettings重新计算:

val paramsFlow = _paramsFlow.asStateFlow()

archivedSettingsFlow = combine(
    typesFlow,
    sortByFlow,
    directionFlow,
    viewSettingsFlow,
    paramsFlow
) { types, sortBy, direction, viewSettings, params ->
    if (params.archivedMode) {
        _paramsFlow.update { it.copy(currentPage = 0, hasNextPage = true) }
    }
    // 构造ArchivedSettings逻辑不变
}

5. 验证debounce的必要性

如果过滤条件修改是用户手动操作(如点击选择),操作间隔通常大于250ms,debounce可能无意义甚至导致事件丢失。可暂时注释该操作,观察问题是否消失:

archivedSettingsFlow = combine(/* ... */) { /* ... */ }
    // .debounce(250L) 暂时注释
    .shareIn(viewModelScope, SharingStarted.WhileSubscribed(5000L), replay = 1)

内容的提问来源于stack exchange,提问作者chrisChris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 06:55:48