Categories
blog kotlin

StateFlow

Intro

If you need a refresher on hot vs cold flows check out the first article in the series

Hot flow

StateFlow is a hot flow and that means it will be alive and running even though subscriber count is zero. It’s very useful for state management since we want the state to be up to date even if no one is observing it so that when a new subscriber comes the latest, the correct state can be shown on the screen. Another nice feature of StateFlow is the .value parameter, it’s an easy and convenient way of reading the latest value without the need for launching coroutines.

Do we need a hot flow for state management?

If we think about the state of a screen and its logic, let’s say whether a user is signed in or not, does that state depend on an observer? Should it? It should not. If a user completes the sign-in flow, it does not matter if there was an observer to that event, we should be able to persist the fact that the user signed in. Otherwise, we would always need an observer to keep the state “correct, which is not ideal. That is why we need hot flow for state management, and we need the ability to broadcast the latest state to any new subscriber. 

Do we need StateFlow?

Can we just use regular Flow and let a flow handle app states? The problem with regular flows is that they are cold, they are not active if they don’t have any observers/subscribers. This is problematic since we want our state to be up to date and be available at all times. Regular flows are not a good use case for state management. StateFlow on the other hand is the opposite, it is a hot flow which means it will hold its value no matter if there are observers or not.

When to use it?

When we need to keep any kind of state and we want the last value to be accessible directly (through .value) StateFlow is idea for that

When not to use it?

In case we need something to behave like a cold flow. One good example of that is error events, we don’t want to keep errors and show them multiple times, ideally, we would show it only if there are observers and we don’t wanna keep errors stored because that would mean we could potentially show the same error multiple times. This was a common issue with LiveData, it needed a hack (SingleLiveData) in order to show an event only once. 

In the next article, we will explore the world of SharedFlow and how we can leverage it’s API

Categories
android blog kotlin

A basic comparison of LiveData, StateFlow, and SharedFlow

Why do we need these classes?

For one simple reason, reactivity. It’s very easy to keep the UI in sync when we don’t have to pull data from the View, instead, View is updated every time there is a change in the screen state. In a sense, this is an idea of unidirectional data flow. There is a single source of truth and everything is reacting to changes in the source. But we need more than that on mobile, we need to stop listening to updates when Views go out of scope to avoid null pointer exceptions. So we need a nice reactive pattern but we also need the ability to automatically unsubscribe from the updates if a particular Activity/Fragment is no longer visible. LiveData is essentially that. StateFlow and SharedFlow are different bests, and they are more like RxJava observables, (in Kotlin terms flow) but can be retrofitted to do the same thing.

Two fundamental types of streams 

If we step back and think about a stream of data, we fundamentally need two different cases, one where we want to grab every event and we don’t want the stream to start before we are ready, aka cold stream. The alternative to this is a hot stream, where we don’t care about consumers of the data, we are emitting values independently. It’s already clear which stream we would prefer for state and which for, let’s say error handling or one-off toast. StateFlow is a hot stream with the last value stored, any new subscriber will be immediately notified of the last value that a StateFlow emitted, making it ideal for keeping track of states in an app screen. Why is that the case? Well, we want to be able to always get the latest state for the screen so we can render the correct UI. Otherwise, we might render an outdated state, or we might be missing state till the next update comes. In contrast to that, let’s say we want to show an error message, we don’t wanna repeat the last error message every time we start listening for error messages. It makes sense to only display the same error message once.

Those are two fundamental use cases for mobile apps. So in a general sense, we are interested in hot streams, since we want to have the latest state processed regardless of the state of a particular Activity/Fragment.

LiveData is not enough

Didn’t we already have this with LiveData? It keeps the last value, it also updates its subscribers every time there is a new state, why do we need State or SharedFlow? Well, for starters LiveData was not suitable for errors and one-off message handling. In the end, a special Event data structure was needed to keep track of processed events. Another issue with LiveData is the lack of operators that it supports, operators like reply, debounce, filter, etc. It was a great solution for Android, but with StateFlow it kinda lost its place. StateFlow is an almost drop-in replacement for LiveData, and it’s supported with Kotlin out of the box. 

StateFlow

It can be called LiveData on steroids. It has similar properties in that it always has the last state saved but it also provides a rich API of operators. One shortcoming of StateFlow is that it does reply last event to new subscribers, which is ideal for state management, but not for one-off events. That is where SharedFlow comes in. 

StateFlow has a value property that can be read at any point in the life of a StateFlow, which is very handy when we need to access the latest state without subscribing. Because it has .value, StateFlow does require an initial value to be provided at the start. This is a bit cumbersome sometimes, but on the other hand, it’s nice to have null/nonnull .value property to be accessed at any point in the future.

Sharedflow

This is a hot flow that just emits values regardless of new subscribers. It can be configured to behave as StateFlow (practice question, how?) but in general, it does not have an initial value and it does not have .value property that can be accessed. By now, you should get a sense that SharedFlow is perfect for one-off events, where we don’t want to keep track of old states, we just wanna listen to new events and handle them as they go. 

Summary

So, how to pick a perfect Observable data structure for an Activity/Fragment? In simple terms, StateFlow is the default choice, if you get the feeling that there is a need for a one-off event, then SharedFlow is the way to go.

In the next couple of articles, we will go a bit more in-depth on StateFlow and SharedFlow. Happy coding!