Replacing LiveData with StateFlow
Introduction
With the rise of KMM (Kotlin multiplatform mobile) hype, how about cleaning up as much Android dependencies as we can to increase the platform-independent code?
LiveData is widely used by Android community to manage and represent screen states. In order to safely replace it, we need to consider some implementation details.
LiveData
Developing for Android is complex for several reasons, and one of them is working with the lifecycle of its main components: Activity and Fragment. Not handling the lifecycle properly may lead to memory leaks, crashes and unexpected states. LiveData is a lifecycle-aware observer implementation that reduces the effort required to deal with these complexities.
StateFlow
Kotlin is a powerful language and can be way more powerful when working with Coroutines. Both of them are constantly evolving and on Coroutines 1.3.6 release, StateFlow was introduced. It is designed to handle state publication scenarios, making it a good candidate to replace LiveData.
A Flow that represents a read-only state with a single updatable data value that emits updates to the value to its collectors
Code
Using LiveData
I'll address the main use-case of LiveData: handling view states using a ViewModel and a Fragment. Below, a simple implementation:
Using StateFlow
First, we need to add these dependencies:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
The final code looks like this:
Some differences to be noticed:
Always has a valueStateFlow
always has a value that can be safely read at any time via value
property. When instantiating StateFlow
, we must provide an initial value that ensures it will starts with a state. Good design in my opinion!
lifecycleScope
By using viewLifecyleOwner.lifecycleScope
extension, we make the flow consumption lifecycle-aware, just likeLiveData
does. On destroy, the coroutine context is cancelled.
launchWhenStartedLiveData
only emits when the LifecycleOwner
is on active state. It pauses its consumption if the lifecycle state is “lower” than Started. In order to replicate this behaviour, we need to use launchWhenStarted
.
Additional information
- At the time of writing, Android Studio did not gave me the correct hint for importing the
collect
extension. You should manually add it if the same happens to you.
import kotlinx.coroutines.flow.collect
- S̶t̶a̶t̶e̶F̶l̶o̶w̶ ̶A̶P̶I̶ ̶i̶s̶ ̶u̶n̶d̶e̶r̶ ̶@̶E̶x̶p̶e̶r̶i̶m̶e̶n̶t̶a̶l̶C̶o̶r̶o̶u̶t̶i̶n̶e̶s̶A̶P̶I̶ ̶b̶u̶t̶ ̶a̶s̶ ̶s̶t̶a̶t̶e̶d̶ ̶i̶t̶ ̶i̶s̶ ̶h̶i̶g̶h̶l̶y̶ ̶u̶n̶l̶i̶k̶e̶l̶y̶ ̶t̶h̶a̶t̶ ̶t̶h̶e̶ ̶c̶o̶r̶e̶ ̶o̶f̶ ̶t̶h̶e̶ ̶d̶e̶s̶i̶g̶n̶ ̶i̶s̶ ̶g̶o̶i̶n̶g̶ ̶t̶o̶ ̶c̶h̶a̶n̶g̶e.
StateFlow API is now stable! v1.4.0 - I've also written an article for
SingleLiveEvent
replacement. Check it out!
Further reading
Introduce StateFlow
The initial design and discussion of StateFlow. Good source of information!