在两个片段之间的 ViewModel 中共享 MutableStateFlow 的冷流
Sharing a cold flow of MutableStateFlow in ViewModel between two fragments
我在两个片段之间共享 MutableStateFlow 属性 时遇到问题。
为了便于理解:
我有一个 BasicViewModel
由于导航图的实现,它应该始终是两个片段的一个实例
private val basicViewModel: basicViewModel by navGraphViewModels(R.id.basic_graph) { defaultViewModelProviderFactory }
此 ViewModel 有一个 MutableStateFlow
属性 声明如下
private val _basicProperty = MutableStateFlow<BasicClass?>(null)
val basicProperty : Flow<BasicClass?> = _basicId
.filterNotNull()
.flatMapConcat { someRepository.getBasicProperty(it) }
.onEach { _basicProperty.value = it }
.catch { }
然后,我使用类似调用 属性 的导航图在导航中声明了 FragmentA
和 FragmentB
,就像这样
basicViewModel.basicProperty
.filterNotNull()
.mapNotNull { it.innerProperty}
.onEach { doSomething(it) }
.launchIn(viewLifecycleOwner.lifecycleScope)
一切看起来都很好,但是当我导航到 FragmentA
BasicProperty 加载流(从 WebApi 加载数据)时,我导航到 FragmentB
并加载流再次而不是调用已经加载的数据,在应用程序中它看起来有点滞后,因为 reload
问题:我应该如何do/change从FragmentB
中的BasicViewModel
获取已经存在的数据?
您的 _basicProperty
是一个热门的 StateFlow,但您从未使用它来收集任何东西。你暴露的属性,basicProperty
,是一个冷流,所以每一个订阅它的人都会开启一个新的运行冷流。这些冷流中的每一个都会将它们的更新发布到 MutableStateFlow,因此此时,它的状态是不可预测的,因为它显示了共享冷流的任何收集器正在做的最新事情。
我认为您想要的是共享一个执行流程。因此,您应该有一个执行连接的 StateFlow,如下所示:
val basicProperty : StateFlow<BasicClass?> = _basicId
.filterNotNull()
.flatMapConcat { someRepository.getBasicProperty(it) }
.catch { }
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
您的原始代码不会执行任何操作来启动流程,直到每个片段都来收集它。但是此代码对 stateIn
的调用在 viewModelScope
中启动了一次流程(在本例中由于 Eagerly
参数而立即启动)。
现在这个流量只有 运行s 一次。您仍然可以让每个片段 运行 像您已经在做的那样拥有自己的下游流。
我在两个片段之间共享 MutableStateFlow 属性 时遇到问题。
为了便于理解:
我有一个 BasicViewModel
由于导航图的实现,它应该始终是两个片段的一个实例
private val basicViewModel: basicViewModel by navGraphViewModels(R.id.basic_graph) { defaultViewModelProviderFactory }
此 ViewModel 有一个 MutableStateFlow
属性 声明如下
private val _basicProperty = MutableStateFlow<BasicClass?>(null)
val basicProperty : Flow<BasicClass?> = _basicId
.filterNotNull()
.flatMapConcat { someRepository.getBasicProperty(it) }
.onEach { _basicProperty.value = it }
.catch { }
然后,我使用类似调用 属性 的导航图在导航中声明了 FragmentA
和 FragmentB
,就像这样
basicViewModel.basicProperty
.filterNotNull()
.mapNotNull { it.innerProperty}
.onEach { doSomething(it) }
.launchIn(viewLifecycleOwner.lifecycleScope)
一切看起来都很好,但是当我导航到 FragmentA
BasicProperty 加载流(从 WebApi 加载数据)时,我导航到 FragmentB
并加载流再次而不是调用已经加载的数据,在应用程序中它看起来有点滞后,因为 reload
问题:我应该如何do/change从FragmentB
中的BasicViewModel
获取已经存在的数据?
您的 _basicProperty
是一个热门的 StateFlow,但您从未使用它来收集任何东西。你暴露的属性,basicProperty
,是一个冷流,所以每一个订阅它的人都会开启一个新的运行冷流。这些冷流中的每一个都会将它们的更新发布到 MutableStateFlow,因此此时,它的状态是不可预测的,因为它显示了共享冷流的任何收集器正在做的最新事情。
我认为您想要的是共享一个执行流程。因此,您应该有一个执行连接的 StateFlow,如下所示:
val basicProperty : StateFlow<BasicClass?> = _basicId
.filterNotNull()
.flatMapConcat { someRepository.getBasicProperty(it) }
.catch { }
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
您的原始代码不会执行任何操作来启动流程,直到每个片段都来收集它。但是此代码对 stateIn
的调用在 viewModelScope
中启动了一次流程(在本例中由于 Eagerly
参数而立即启动)。
现在这个流量只有 运行s 一次。您仍然可以让每个片段 运行 像您已经在做的那样拥有自己的下游流。