【问题标题】:How to collect multiple state flow in Android如何在Android中收集多个状态流
【发布时间】:2021-05-25 13:46:14
【问题描述】:

如何在activity中采集两个状态流?因为我只使用了第一个消耗的流量。

比如viewmodel里面是这样的:

class ExampleViewModel: ViewModel(){
    private val state = MutableStateFlow<HomeMainFragmentState>(HomeMainFragmentState.Init)
    private val products = MutableStateFlow<List<ProductEntity>>(mutableListOf())


    //to be consumed
    fun getState() : StateFlow<HomeMainFragmentState> = state
    fun getProducts() : StateFlow<List<ProductEntity>> = products
}

然后在我看来是这样的:

private fun observe(){
      viewLifecycleOwner.lifecycleScope.launch {
      viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){
            viewModel.getState().collect { state -> handleState(state) }
            viewModel.getProducts().collect{ products -> handleProducts(products) }
        }
    }
}

问题是,只有第一个流被消耗,对于这种情况是“状态”产品从未被活动/片段消耗/执行。

如何解决这个问题? 我还阅读了有关合并流程的信息,这是否意味着第二个流程取决于第一个流程来运行?

【问题讨论】:

    标签: android kotlin android-livedata kotlin-flow


    【解决方案1】:

    您必须为每个收集器使用CoroutineScope.launch 函数。

    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){
            launch {
                viewModel.getState().collect { state -> handleState(state) }
            }
            launch {
                viewModel.getProducts().collect{ products -> handleProducts(products) }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      在 Flow 上调用 collect 会暂停协程,直到 Flow 完成。对于 MutableStateFlow,只有在取消时才会完成。因此,通常,当您在 Flow 上调用 collect 时,您不会在该调用下的协程中执行任何其他操作。

      如果你想单独收集这些流中的每一个,你需要两个协程。

      flowOnLifecycle 函数将为您提供更简洁的代码,因此拥有两个协程并不那么痛苦:

      private fun observe(){
          viewModel.getState()
              .flowOnLifecycle(Lifecycle.State.STARTED)
              .onEach { state -> handleState(state) }
              .launchIn(lifecycleScope)
          viewModel.getProducts()
              .flowOnLifecycle(Lifecycle.State.STARTED)
              .onEach { products -> handleProducts(products) }
              .launchIn(lifecycleScope)
      }
      

      我还想提一下,像 getState 这样的函数名称在 Kotlin 中是不自然的,除非它们是重函数(即使它是一个需要计算的重函数,我更喜欢 generateState 或 @987654327 @)。使用属性更合适:

      private val mutableState = MutableStateFlow<HomeMainFragmentState>(HomeMainFragmentState.Init)
      val state: StateFlow<HomeMainFragmentState> get() = mutableState
      

      他们说过,在 未来 版本的 Kotlin 中,可能会有更好的语法来公开不需要第二个属性的可变类的只读版本。像这样的:

      private val state = MutableStateFlow<HomeMainFragmentState>(HomeMainFragmentState.Init)
          public get(): StateFlow<HomeMainFragmentState>
      

      【讨论】:

      • 啊我明白了,那我需要两个协程。根据您的建议更改我的视图模型:private val state = MutableStateFlow&lt;HomeMainFragmentState(HomeMainFragmentState.Init) val pState: StateFlow&lt;HomeMainFragmentState&gt; get() = state,然后像这样使用它:` viewModel.pState.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED).onEach { state -> handleState(state) }.launchIn(viewLifecycleOwner.lifecycleScope )` 解决了我的问题。它也更具可读性。谢谢
      猜你喜欢
      • 2021-08-20
      • 1970-01-01
      • 1970-01-01
      • 2019-05-21
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      相关资源
      最近更新 更多