Image for post
Image for post
Photo by Pablo García Saldaña on Unsplash

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.

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.

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

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

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.

  • 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!

Introduce StateFlow
The initial design and discussion of StateFlow. Good source of information!

Written by

Android Developer @iFood

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store