In coroutines, a flow is a type that can emit multiple values sequentially, as opposed to suspend functions that return only a single value. For example, you can use a flow to receive live updates from a database.
Flows are built on top of coroutines and can provide multiple values. A flow is conceptually a stream of data that can be computed asynchronously. The emitted values must be of the same type. For example, a Flow<Int> is a flow that emits integer values.
o create flows, use the flow builder APIs. The flow builder function creates a new flow where you can manually emit new values into the stream of data using the emit function.
In the following example, a data source fetches the latest news automatically at a fixed interval. As a suspend function cannot return multiple consecutive values, the data source creates and returns a flow to fulfill this requirement. In this case, the data source acts as the producer.
Intermediaries can use intermediate operators to modify the stream of data without consuming the values. These operators are functions that, when applied to a stream of data, set up a chain of operations that aren't executed until the values are consumed in the future
In the example below, the repository layer uses the intermediate operator map to transform the data to be displayed on the View:
Intermediate operators can be applied one after the other, forming a chain of operations that are executed lazily when an item is emitted into the flow. Note that simply applying an intermediate operator to a stream does not start the flow collection.
Use a terminal operator to trigger the flow to start listening for values. To get all the values in the stream as they're emitted, use collect.
As collect is a suspend function, it needs to be executed within a coroutine. It takes a lambda as a parameter that is called on every new value. Since it's a suspend function, the coroutine that calls collect may suspend until the flow is closed.
Continuing the previous example, here's a simple implementation of a ViewModel consuming the data from the repository layer:
Collecting the flow triggers the producer that refreshes the latest news and emits the result of the network request on a fixed interval. As the producer remains always active with the while(true) loop, the stream of data will be closed when the ViewModel is cleared and viewModelScope is cancelled.
Flow is integrated into many Jetpack libraries, and it's popular among Android third party libraries. Flow is a great fit for live data updates and endless streams of data.
You can use Flow with Room to be notified of changes in a database. When using data access objects (DAO), return a Flow type to get live updates.
Every time there's a change in the Example table, a new list is emitted with the new items in the database.
callbackFlow is a flow builder that lets you convert callback-based APIs into flows. As an example, the Firebase Firestore Android APIs use callbacks.
To convert these APIs to flows and listen for Firestore database updates, you can use the following code:
StateFlow and SharedFlow are Flow APIs that enable flows to optimally emit state updates and emit values to multiple consumers.
In Android, StateFlow is a great fit for classes that need to maintain an observable mutable state.
StateFlow is a state-holder observable flow that emits the current and new state updates to its collectors. The current state value can also be read through its value property. To update state and send it to the flow, assign a new value to the value property of the MutableStateFlow class.
Following the examples from Kotlin flows, a StateFlow can be exposed from the LatestNewsViewModel so that the View can listen for UI state updates and inherently make the screen state survive configuration changes.
StateFlow and LiveData have similarities. Both are observable data holder classes, and both follow a similar pattern when used in your app architecture.
Note, however, that StateFlow and LiveData do behave differently:
StateFlow requires an initial state to be passed in to the constructor, while LiveData does not.
LiveData.observe() automatically unregisters the consumer when the view goes to the STOPPED state, whereas collecting from a StateFlow or any other flow does not stop collecting automatically. To achieve the same behavior, you need to collect the flow from a Lifecycle.repeatOnLifecycle block.
Yet to learn