在两个片段之间共享 ViewModel 中的 MutableStateFlow 冷流

问题描述

我遇到了在两个片段之间共享的 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 {  }

然后,我使用导航图在导航中声明了 FragmentAFragmentB,这些导航图类似地调用了该属性,就像这样

        basicviewmodel.basicProperty
        .filterNotNull()
        .mapNotNull { it.innerProperty}
        .onEach { doSomething(it) }
        .launchIn(viewLifecycleOwner.lifecycleScope)

一切看起来都很好,但是当我导航到 FragmentA 时,BasicProperty 加载流程(从 WebApi 加载数据)然后导航到 FragmentB 并且流程再次加载而不是调用已经加载的数据在应用中看起来有点滞后,因为重新加载

问题:我应该做什么/更改以从 Basicviewmodel 中的 FragmentB 获取现有数据?

解决方法

您的 _basicProperty 是一个热门的 StateFlow,但您从不使用它来收集任何东西。您公开的属性 basicProperty 是一个冷流,因此每个收集它的订阅者都将开始新的冷流运行。这些冷流中的每一个都会将其更新发布到 MutableStateFlow,因此在这一点上,其状态是不可预测的,因为它显示了共享冷流的任何收集器正在执行的最新操作。

我认为您想要的是共享一个执行流程。因此,您应该有一个执行连接的 StateFlow,如下所示:

val basicProperty : StateFlow<BasicClass?> = _basicId
    .filterNotNull()
    .flatMapConcat { someRepository.getBasicProperty(it) }
    .catch {  }
    .stateIn(viewModelScope,SharingStarted.Eagerly,null)

在每个 Fragment 来收集它之前,您的原始代码不会启动流程。但此代码对 stateIn 的调用在 viewModelScope 中启动了一次流(在本例中,由于 Eagerly 参数而立即)​​。

现在这个流程只运行一次。您仍然可以像之前那样让每个 Fragment 运行自己的下游流。