I have a state flow that emits a value once the HTTP request is returned with the response, the value will be shown as a list in Activity, I'm using Kotlin coroutines StateFlow
for communication between the ViewModel and Activity.
I'm using androidx
lifecycle repeatOnLifecycle
function like this:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.successFlow.collect { binding.recyclerView.adapter = ExampleAdapter(it) }
}
}
This is working fine at the beginning but then I realized that every time the user goes to another screen and back to the previous screen the state flow will reemit the value which in this case will lose the list state, for example, if the user scrolled to item 10
in the list and then goes to another screen and return back the list will scroll to position 0
because the setAdapter
method invoked again, which is not the case when using LiveData
.
Now I need to handle StateFlow
state and configuration state too, I tried to use the distinctUntilChanged
method but as the documentation says Applying 'distinctUntilChanged' to StateFlow has no effect
.
The question here how I can achieve the same LiveData
behaviour using StateFlow
.
Unlike a cold flow built using the flow builder, a StateFlow is hot: collecting from the flow doesn't trigger any producer code. A StateFlow is always active and in memory, and it becomes eligible for garbage collection only when there are no other references to it from a garbage collection root.
LiveData is an Android Framework If we want to share the common code between different platforms (e.g. iOS and Android, e.g. using Kotlin Multiplatform Module — KMM), using LiveData will make the common code Android-specific. StateFlow is platform-agnostic, and hence more portable to support across platforms.
StateFlow(hot stream) does similar things like LiveData but it is made using flow by kotlin guys and only difference compare to LiveData is it's not lifecycle aware but this is also been solved using repeatOnLifecycle api's, so whatever LiveData can do StateFlow can do much better with power of flow's api.
StateFlow is a state-holder observable flow that emits the current and new state updates to its collectors. In Android, StateFlow is a great fit for classes that need to maintain an observable mutable state.
You have hit the problem that was recently described in a Medium post. Each flow collection starts anew and will receive the value again and again even if it has not changed. LiveData
, on the other hand, knows about which version of the data each observer has seen and will only send data that has been posted again.
A simple fix for your problem would be to store the data somewhere and check if it has changed. You can also collect the flow with flowWithLifecycle()
and do distinctUntilChanged()
on it (beware of using a buffered operator after this or the value emission might be lost, as described in the post linked above).
It would look like this:
viewModel.successFlow.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach {
binding.recyclerView.adapter = ExampleAdapter(it)
}.launchIn(lifecycleScope)
However, I think you should not replace the adapter each time the data changes! You should be doing:
(binding.recyclerView.adapter as ListAdapter<*,*>).submitList(it)
(Of course, you should be using ListAdapter for this to work)
With this nothing will get disrupted when new data arrives, whether is the same or different data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With