If you're getting into the world of Reactive programming with KotlinYou've probably noticed that not all workflows behave the same way. For those less familiar, we're basically talking about whether the workflow is "static," waiting for someone to call it, or "dynamic," constantly pumping data even when no one is looking at it. Managing this effectively is the difference between a lightning-fast app and one that eats up device memory.
In this article we're going to thoroughly explain how to switch from the default cold flows to hot flows using stateIn and sharedInWe're not just going to stay in the theory; we're going to see how to implement it in ViewModel and, most importantly, how to prevent unit tests from becoming a nightmare when data isn't output as expected.
Understanding the difference between cold and hot flows
To get off to a good start, you have to understand that a Cold Flow is like a song on Spotify that starts from second zero every time you press play; that is, each collector receives its own sequence They handle data independently. They are ideal for resource-intensive tasks that only need to be executed when someone is listening, such as a database query.
On the other hand, Hot Flows are more like a radio station: the music plays regardless of whether you have the radio on or not. Here, Multiple subscribers share the same stream of data. These are fundamental when you need to manage the state of the user interface or trigger events that need to reach several parts of the application simultaneously.
StateFlow: The Guardian of the State
StateFlow is a type of hot flow specialized in maintaining a specific state. Its main characteristic is that always retains the most recent valueThis makes it the perfect tool to replace the old LiveData on Android. For it to work, it requires an initial value in its constructor, ensuring that the UI always has something to display.
When working with a ViewModel, we typically use a private MutableStateFlow to change the value internally and expose a public StateFlow so the view can only read it. It's vital to remember that StateFlow combines emissionsIf the values ​​change too quickly, the collector might skip some intermediate states and receive only the latest one, which is efficient for the user interface.
SharedFlow: Global Event Broadcaster
Unlike the previous one, SharedFlow does not store a current state by default, but is used to broadcast events to multiple subscribersImagine you need to notify the entire app that a user has logged out; a SharedFlow is the ideal option because it allows you to configure the replaywhich defines how many old values ​​are sent to new subscribers.
Furthermore, it offers comprehensive control over back pressure through the parameter onBufferOverflowYou can decide whether the sender should be suspended when the buffer is full, or whether you prefer to discard the oldest or newest item to avoid blocking program execution.
Transforming cold flows with stateIn and sharedIn
Sometimes we have a cold flow that's extremely expensive to maintain (like a persistent network connection) and we don't want each subscriber to open a new connection. This is where the operator comes in. stateInwhich converts a cold flow into a shared hot StateFlow. This operator needs a CoroutineScope and a startup strategy.
The configuration of SharingStarted It is key: Lazily Collection begins with the first subscriber and does not stop, while WhileSubscribed It's the smartest option for Android, because stops data production when no one is listening (for example, when the app is in the background), saving battery and memory.
If you need a SharedFlow instead of a StateFlow, the operator to use is shareInThis works similarly but is more flexible, allowing you to define exactly how many elements should be relayed to new members of the flow using the parameter replay.
Testing Strategies for Hot Flows
Testing these flows is tricky. When the test subject is the observer of the flow, it's best to create fake implementations from repositories that emit controlled values. If the module exposes the flow, we can use functions like first() to validate the first issue or toList() for finite flows.
A common problem with stateIn That is, if we use WhileSubscribedThe test may fail because the flow is not activated if there is no collector. To fix this, we must launch an empty collector at the backgroundScope of the test, ensuring that the operator is activated and updates the values. To simplify the process, the use of the library is recommended. Impeller, which allows emissions to be validated sequentially and in a much more natural way.
- For StateFlows, the recommendation is to directly validate the property value instead of collecting the flow, thus avoiding problems with combining fast values.
- It is essential to use UnconfinedTestDispatcher to ensure that the collection coroutine is ready before the data is issued in the test.
Mastering the transition between cold and hot flows through the correct application of stateIn and sharedIn allows you to build much more robust architectures in Android, optimizing resource consumption and ensuring that data synchronization between business logic and user interface is flawless and easy to test. Share this information so that more people can learn about the topic.