如何实现Fragment在可见时仅收集ViewModel的最新事件?
问题:如何让Fragment仅收集ViewModel生成的最新事件?
我正尝试以最高效的方式收集ViewModel生成的事件,需要满足以下两个特性:
- 当视图不再可见时,Fragment不应收集事件;
- 当视图可见时,若存在新事件则仅收集最新的那个,否则不触发事件。
第一个特性用lifecycleScope.launchWhenStarted就能实现,但第二个特性我搞不定。
MyViewModel 代码:
@HiltViewModel class MyViewModel @Inject constructor( ): ViewModel() { private val eventChannel = Channel<String>(capacity = Channel.CONFLATED) val eventFlow = eventChannel.receiveAsFlow() fun triggerEvent(event: String) { viewModelScope.launch { eventChannel.send(event) } } }
MyFragment 代码:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding.buttonGoScanProductFragment.setOnClickListener { lifecycleScope.launch { viewModel.triggerEvent(generateUniqueString(10)) delay(5000) viewModel.triggerEvent(generateUniqueString(10)) delay(5000) viewModel.triggerEvent(generateUniqueString(10)) delay(5000) viewModel.triggerEvent(generateUniqueString(10)) delay(5000) viewModel.triggerEvent(generateUniqueString(10)) delay(5000) viewModel.triggerEvent(generateUniqueString(10)) delay(5000) } } lifecycleScope.launchWhenStarted { viewModel.eventFlow.collectLatest{ Timber.d("Event collected: $it") } } return binding.root }
总结问题:如何让Fragment仅收集最新生成的事件?
我不使用SharedFlow的原因是,据我所知当视图可见时它不会提供最后一个事件。
解决方案
你之前对SharedFlow的理解有误,通过调整它的配置完全可以满足需求,而且实现起来更贴合Jetpack的响应式规范。
修改后的ViewModel
@HiltViewModel class MyViewModel @Inject constructor(): ViewModel() { // replay=1:新订阅者会收到最新的1个事件 // extraBufferCapacity=0 + DROP_OLDEST:事件发送时若缓冲区满,直接丢弃旧事件,只保留最新的 private val _eventFlow = MutableSharedFlow<String>( replay = 1, extraBufferCapacity = 0, onBufferOverflow = BufferOverflow.DROP_OLDEST ) val eventFlow = _eventFlow.asSharedFlow() fun triggerEvent(event: String) { viewModelScope.launch { _eventFlow.emit(event) } } }
Fragment收集逻辑保持不变
lifecycleScope.launchWhenStarted { viewModel.eventFlow.collectLatest { Timber.d("Event collected: $it") } }
核心逻辑说明
- 满足视图不可见时不收集:
launchWhenStarted会在Fragment进入STOPPED状态时挂起收集,STARTED状态时恢复,完全符合你的第一个要求。 - 仅收集最新事件:
extraBufferCapacity=0意味着没有额外缓冲区,新事件进来时如果当前没有活跃的收集器,会触发溢出策略;DROP_OLDEST策略会直接丢弃旧的未处理事件,只保留最新的那个;replay=1保证当Fragment重新可见恢复收集时,会立即收到最新的事件。
这个配置的SharedFlow和你之前用的CONFLATED Channel效果一致,但SharedFlow在生命周期感知场景下的适配性更好,也更符合Flow的使用习惯。
内容的提问来源于stack exchange,提问作者Qwe Qwe
相关产品推荐
相关产品推荐

