Photo by Pablo García Saldaña on Unsplash

Replacing LiveData with StateFlow

Cesar Morigaki

--

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:

ViewModel managing LiveData
Observing LiveData at MainFragment

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:

ViewModel managing StateFlow
Observing StateFlow at MainFragment

Some differences to be noticed:

Always has a value
StateFlow 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.

launchWhenStarted
LiveData 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!

--

--