Flow onEach/Collect在从片段返回时被多次调用

我使用Flow而不是LiveData来收集片段中的数据。在片段A中,我观察(或者更确切地说是收集)片段的onView中的数据如下所示:

lifecycleScope.launchWhenStarted {
            availableLanguagesFlow.collect {
                languagesAdapter.setItems(it.allItems, it.selectedItem)
            }
        }

问题。然后,当我转到片段B,然后返回到片段A时,我的Collect函数被调用两次。如果我再次访问片段B并返回到A,则收集函数被调用3次。以此类推。


解决方案

原因

它的发生是因为tricky Fragment lifecycle。当您从片段B返回到片段A时,片段A会重新连接。结果,片段的onViewCreated被第二次调用,并且您第二次观察到流的相同实例。换句话说,现在您有一个具有两个观察者的流,当该流发出数据时,就会调用其中两个观察者。

片段的解决方案%1

在片段的onViewCreated中使用viewLifecycleOwner。更具体地说,使用viewLifecycleOwner.lifecycleScope.launch而不是lifecycleScope.如下所示:

viewLifecycleOwner.lifecycleScope.launchWhenStarted {
            availableLanguagesFlow.collect {
                languagesAdapter.setItems(it.allItems, it.selectedItem)
            }
        }

活动的解决方案2

在活动中,您只需在onCreate中收集数据。

lifecycleScope.launchWhenStarted {
            availableLanguagesFlow.collect {
                languagesAdapter.setItems(it.allItems, it.selectedItem)
            }
        }

其他信息

  1. LiveData也是如此。见帖子here。也请勾选this article。
  2. 使用Kotlin扩展使代码更干净:

分机:

fun <T> Flow<T>.launchWhenStarted(lifecycleOwner: LifecycleOwner) {
    lifecycleOwner.lifecycleScope.launchWhenStarted {
        this@launchWhenStarted.collect()
    }
}

在片段onView Created中:

availableLanguagesFlow
    .onEach {
        //update view
    }.launchWhenStarted(viewLifecycleOwner)

更新

我更喜欢使用NowrepeatOnLifecycle,因为它会在生命周期低于状态(在我的例子中是onStop)时取消正在进行的协程。如果没有repeatOnLifecycle,则onStop时将暂停采集。签出this article。

fun <T> Flow<T>.launchWhenStarted(lifecycleOwner: LifecycleOwner)= with(lifecycleOwner) {
    lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED){
            try {
                this@launchWhenStarted.collect()
            }catch (t: Throwable){
                loge(t)
            }
        }
    }
}

相关文章