I’ve been looking more into Kotlin coroutines last week. The previous article focused on some of the fundamentals of coroutines such as CoroutineContext, CoroutineScope, Coroutine Builder etc. As promised, this is a follow up to that on Flows.
What are Flows?
A stream of data that can be computed asynchronously is referred to as a Flow . Flow, like LiveData and RxJava streams, allows you to implement the observer pattern: a software design pattern consisting of an object (source) that keeps a list of its dependents, called observers (collectors) and automatically notifies them of any state changes. A Flow uses suspend functions to produce and consume values asynchronously.
To create a Flow, first you need to create a producer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
To collect flow, first you will launch a Coroutine because flow operates on Coroutines under the hood. The collect operator is used to collect the values emitted by it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Cold Flow — It does not start producing values until one starts to collect them. It can have only one subscriber and it does not store data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Hot Flow — It will produce values even if no one is collecting them. It can have multiple subscribers and it can store data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
We can convert any cold flow to a hot one using the stateIn() and shareIn() operators respectively.
SharedFlow & StateFlow
StateFlow — A StateFlow is a HotFlow that represents a state holding a single value at a time. It is also a conflated flow, meaning that when a new value is emitted, the most recent value is retained and immediately emitted to new collectors. It is useful when you need to maintain a single source of truth for a state and automatically update all the collectors with the latest state. It always has an initial value and only stores the latest emitted value.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SharedFlow — A SharedFlow is a HotFlow that can have multiple collectors. It can emit values independently of the collectors, and multiple collectors can collect the same values from the flow. It’s useful when you need to broadcast a value to multiple collectors or when you want to have multiple subscribers to the same stream of data. It does not have an initial value, and you can configure its replay cache to store a certain number of previously emitted values for new collectors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Kotlin Flow offers several mechanisms for handling exceptions and errors.
try-catch Blocks — One of the fundamental ways to handle exceptions is by using try-catch blocks within your flow.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
catch Operator — The catch operator in Flow allows you to handle exceptions by encapsulating the error-handling logic in one place.
flow { emit(productsService.fetchProducts()) }.catch { e -> emitError(e) }
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
onCompletion Operator — is used for executing code after the Flow completes, whether it completes successfully or with an exception.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Custom Error Handling — In complex scenarios in Android, we can create custom operators or extension functions to handle errors in a way that suits our application.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LiveData is lifecycle-aware, which means it automatically manages the lifecycle of the observers, ensuring that updates are only delivered when the observer is in an active state. Flow, on the other hand, is not lifecycle-aware by default. We can use the collectLatest() or collectAsStateWithLifecycle() functions in Compose to collect results from Flow.
Flow provides more flexibility and is suitable for more complex asynchronous data operations, while LiveData is typically used for simpler UI updates.
Flow provides built-in support for backpressure, allowing control over the rate of data emission and processing, whereas LiveData doesn’t support backpressure handling.
Flow offers a rich set of operators for sequential and structured processing, while LiveData focuses on delivering the latest data to observers.
Thanks for reading guys! I hope you enjoyed this article and found it useful. Let me know your thoughts in the comments section.
Leave a comment