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 -> 第二次发射正确新值 多次操作后,只会触发首次发射的旧值
问题分析与解决方案
核心原因排查
SharingStarted.Lazily特性限制:该策略会在第一个订阅者出现时启动流,所有订阅者取消后流会停止。Fragment从返回栈恢复时,重新订阅只会重播最后一次缓存值,若上游的过滤条件流未发射新值,combine不会重新计算新的ArchivedSettings。- 上游流可能是冷流:如果
filtersRepository返回的是冷流(每次订阅重新获取数据而非持续监听变化),修改过滤条件后不会主动触发流的新值发射,导致combine无新数据可计算。 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
相关产品推荐
相关产品推荐

