原因
这是因为tricky Fragment lifecycle。当您从片段 B 回到片段 A 时,片段 A 会重新连接。结果片段的 onViewCreated 被第二次调用,您观察到同一 Flow 实例第二次。换句话说,现在你有一个 Flow 有两个观察者,当这个 flow 发出数据时,就会调用其中的两个。
片段解决方案 1
在 Fragment 的 onViewCreated 中使用 viewLifecycleOwner。更具体地说,使用 viewLifecycleOwner.lifecycleScope.launch 而不是生命周期范围.launch。像这样:
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
availableLanguagesFlow.collect {
languagesAdapter.setItems(it.allItems, it.selectedItem)
}
}
活动解决方案 2
在 Activity 中,您可以简单地在 onCreate 中收集数据。
lifecycleScope.launchWhenStarted {
availableLanguagesFlow.collect {
languagesAdapter.setItems(it.allItems, it.selectedItem)
}
}
其他信息
- LiveData 也是如此。见帖子here。另请查看this article。
- 使用 Kotlin 扩展使代码更简洁:
扩展名:
fun <T> Flow<T>.launchWhenStarted(lifecycleOwner: LifecycleOwner) {
lifecycleOwner.lifecycleScope.launchWhenStarted {
this@launchWhenStarted.collect()
}
}
在片段 onViewCreated 中:
availableLanguagesFlow
.onEach {
//update view
}.launchWhenStarted(viewLifecycleOwner)
更新
我宁愿现在使用repeatOnLifecycle,因为当生命周期低于状态时它取消正在进行的协程(在我的例子中是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)
}
}
}
}