为什么状态流多次调用 callectLatest?
Why state flow calls callectLatest multiple times?
所以,我想使用 StateFlow 而不是 LiveData,但我无法弄清楚我的逻辑有什么问题。
我有一个流程,它有一个默认的空值。当我打开一个包含一些数据的对话框时,在我 select 一个数据之后,我将新值发送到流中。
第一次,dialog关闭后调用collectLatest,得到null值(init),emit后得到新值,不错。但是如果我再次打开对话框,并且 select 值,然后关闭对话框,collectLatest 调用了 3 次,然后我再次打开对话框...并且 collectLatest 调用了 4 次,依此类推。
所以这是非常糟糕的行为,我敢肯定,我做错了什么,但我没有看到错误。
在 liveData 中,预期的行为是在对话框关闭后,observer fun 只被调用一次。我想实现这个。
我也检查过,我只发出一次新值,所以没有理由多次触发 collectLatest。
视图模型:
private val _previousManufacture = MutableStateFlow<PreviousManufactureView?>(null)
val previousManufacture = _previousManufacture.asStateFlow()
private suspend fun setPreviousManufactureByMachineId(machineId: String) {
val result = stateReportRepository.getPreviousManufactureByMachineId(machineId)
if (result is Result.Success) {
_previousManufacture.emit(result.data)
} else {
_previousManufacture.emit(null)
}
}
片段:
lifecycleScope.launchWhenCreated {
viewModel.previousManufacture.collectLatest {
var d = it
}
}
[更新]
片段:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.vm = viewModel
initFlows()
}
private fun initFlows() {
lifecycleScope.launchWhenCreated {
viewModel.openStateOfWorkflowBrowser.collectLatest {
openStateOfWorkflowSelectionDialog()
}
}
...
}
抱歉,我之前在评论中错过了这个,但我认为问题在于您在片段的 lifecycleScope
中调用 launchWhenCreated
,而不是在其 viewLifecycle.lifecycleScope
中调用。所以如果 Fragment 被重用(比如在一个对话框片段出现之后),旧的收集器不会被取消并添加一个新的收集器,因为 Fragment 的生命周期还没有结束,只是它之前视图的生命周期。在 Fragment 中使用协程时,几乎应该始终使用 viewLifecycle.lifecycleScope
。
所以,我想使用 StateFlow 而不是 LiveData,但我无法弄清楚我的逻辑有什么问题。
我有一个流程,它有一个默认的空值。当我打开一个包含一些数据的对话框时,在我 select 一个数据之后,我将新值发送到流中。
第一次,dialog关闭后调用collectLatest,得到null值(init),emit后得到新值,不错。但是如果我再次打开对话框,并且 select 值,然后关闭对话框,collectLatest 调用了 3 次,然后我再次打开对话框...并且 collectLatest 调用了 4 次,依此类推。
所以这是非常糟糕的行为,我敢肯定,我做错了什么,但我没有看到错误。
在 liveData 中,预期的行为是在对话框关闭后,observer fun 只被调用一次。我想实现这个。
我也检查过,我只发出一次新值,所以没有理由多次触发 collectLatest。
视图模型:
private val _previousManufacture = MutableStateFlow<PreviousManufactureView?>(null)
val previousManufacture = _previousManufacture.asStateFlow()
private suspend fun setPreviousManufactureByMachineId(machineId: String) {
val result = stateReportRepository.getPreviousManufactureByMachineId(machineId)
if (result is Result.Success) {
_previousManufacture.emit(result.data)
} else {
_previousManufacture.emit(null)
}
}
片段:
lifecycleScope.launchWhenCreated {
viewModel.previousManufacture.collectLatest {
var d = it
}
}
[更新] 片段:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.vm = viewModel
initFlows()
}
private fun initFlows() {
lifecycleScope.launchWhenCreated {
viewModel.openStateOfWorkflowBrowser.collectLatest {
openStateOfWorkflowSelectionDialog()
}
}
...
}
抱歉,我之前在评论中错过了这个,但我认为问题在于您在片段的 lifecycleScope
中调用 launchWhenCreated
,而不是在其 viewLifecycle.lifecycleScope
中调用。所以如果 Fragment 被重用(比如在一个对话框片段出现之后),旧的收集器不会被取消并添加一个新的收集器,因为 Fragment 的生命周期还没有结束,只是它之前视图的生命周期。在 Fragment 中使用协程时,几乎应该始终使用 viewLifecycle.lifecycleScope
。