Home > Software design >  Why state flow calls callectLatest multiple times?
Why state flow calls callectLatest multiple times?

Time:05-24

So, I would like to use StateFlow instead of LiveData, but I can not figure out what's the problem with my logic.

I have a flow, which has a default null value. When I open a dialog which contains a some datas, after that I select one data, I emit the new value to the flow.

In the first time, after the dialog closed, collectLatest called, and I get the null value (init), after the emit, I get the new value, it is good. But If I open the dialog again, and select value, and close the dialog, the collectLatest fun called 3-times, and I again open the dialog... and collectLatest called 4 times and so on.

So this is very bad behavior, and I'm sure , I did something wrong, but I don't see the bug.

In the liveData the expected behavior is after the dialog close, that the observer fun is called just once. I would like to achive this.

I also checked, that I emit the new value only once, so there is no reason why collectLatest fire multiple times.

ViewModel:

    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)
        }
    }

Fragment:

            lifecycleScope.launchWhenCreated {
                viewModel.previousManufacture.collectLatest {
                    var d = it
                }
            }

[Update] Fragment:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.vm = viewModel

        initFlows()
}

 private fun initFlows() {
        lifecycleScope.launchWhenCreated {
            viewModel.openStateOfWorkflowBrowser.collectLatest {
                openStateOfWorkflowSelectionDialog()
            }
        }
...
}

CodePudding user response:

Sorry, I missed this before in my comment, but I think the problem is that you are calling launchWhenCreated in the lifecycleScope of the Fragment, not in its viewLifecycle.lifecycleScope. So if the Fragment is reused (like after a dialog fragment has a appeared), the old collector is not cancelled and a new one is added, because the lifecycle of the Fragment has not ended, only the lifecycle of its previous view. You should almost always use viewLifecycle.lifecycleScope when you are using coroutines in a Fragment.

  • Related