Advanced Coroutines with Kotlin Flow & LiveData

Advanced Coroutines with Kotlin Flow & LiveData
Advanced Coroutines with Kotlin Flow & LiveData

LiveData Coroutine Builder:

The entire coroutine itself as a code block is relayed to the liveData () builder function as an argument. The code block is not enclosed with parenthesis as Kotlin enables us to skip the parenthesis when a code block is dynamically altering, by using LiveData Coroutine along with a switchMap() function. In many cases, developers trigger the coroutine only when the value of LiveData is altered. For instance, whenever an ID is a prerequisite for launching a data load.

There is also another coroutine library for representing asynchronous sequences, or audio and video streams of values known as the Coroutines Asynchronous Flow.

What is Kotlin Flow?

Kotlin Flow is a contemporary stream processing API (Application user interface). The idea for Kotlin Flow comes from Reactive Stream specification, which is an initiative taken to set a uniform standard for asynchronous stream processing.

Analogous to coroutines Kotlin Flow was also developed by the JetBrains, one of the top-notch companies and the developer of Kotlin language also. That’s why the entire concept of Kotlin Flow is based on the structure of Kotlin Coroutines. To structure your data in a complex multi-threaded way with a concise and brief code you may use Kotlin Flow to handle a stream of values.

Kotlin flow is a sequential process, which includes the following steps:

  • Data extraction and data streaming.
  • Invoking synchronous and asynchronous APIs.
  • Streaming Hot and cold data.
  • With the help of try and catch, handling the exceptions amidst data flow.

Kotlin flow to LiveData

You can know more about advanced coroutines with Kotlin Flow and LiveData and include the logical aspect of your code in a LiveData builder. Kotlin Coroutines 1.2.0 comes up with a cold stream called Flow. Kotlin Flow is used for carrying out asynchronous operations. If you try to blend and encapsulate some of the Kotlin Flows, you may transform multiple asynchronous sources.

The most crucial task in mobile development is transferring data, it is achieved by Control concurrency LiveData. Apart from transferring data in a unique way, Kotlin flow also performs async operations. Further exploring LiveData and Kotlin Flow, Kotlin Flow transfers encapsulated data by converting them into an existing LiveData by exploiting the Kotlin coroutines-friendly LiveData builder. You may include the logical segments of the code within a LiveData builder. It makes the co-ordination among UI and ViewModel easier, on the logic designed by you.

Image Source: Medium

On combining Kotlin Coroutines Flow, LiveData and Data Binding you can perform asynchronous operations. Although it is true that Flow is part of Kotlin and LiveData is part of the androidx.lifecycle library yet Flow can be used as an integral component of the uses cases in a clear architecture, without the requirement of any supplementary dependencies. In contrast to Kotlin Flow, LiveData is aware of the life cycle, so it overlaps the ViewModel.

Properties of Flow:

Context Preservation and Backpressure: Flow possesses a unique property known as context preservation., which implies a set of Flow always takes place in the context of the parent coroutine. You may alter the property later while emitting items. The flowOn() function can be used for changing the context of emissions.

Sometimes you can have some unusual scenarios such as a Flow may produce events too fast, so the collector is unable to consume them. This is known as backpressure, in reactive streams. Kotlin Flow supports backpressure extraordinary as it is based on coroutines. The producer has the ability to recognize whether the consumer is busy doing some work or is in the suspended state. At such instances, the producer doesn’t produce any items and waits until the consumer becomes active.

Exceptions: Flow streams end up throwing exceptions if any emitter or codes encapsulated within operators throw an exception. Similar to Java, catch() blocks are used to handle exceptions within Flow also. This can be done in two ways, imperatively or declaratively.

A very widespread example of the imperative approach is the try-catch block on the collector’s side. It is considered to be imperative as these catch the entire range of exceptions that are initialized in the emitter or in any of the operators. While for handling errors declaratively catch() can be used. By declarative, we mean that you have to declare a function to deal with the errors. In cases of Flow, you can declare the catch() within the flow, you don’t need to embrace it with a try-catch block.

Cancellations: You can observe the forecasts in Model.kt. You must be wondering that for how long you may observe this. As soon as the LiveData becomes active the Flow collection begins. In such cases, if LiveData turns inactive prior to the Flow completion, the flow collection gets cancelled. The cancelled is triggered after a specifically timed delay unless LiveData turns active again prior to the timeout.5000 milliseconds is the default delay triggering cancellation.

You can overwrite the timeout value according to your requirements. Android configuration changes can be handled efficiently by using such timeouts. If LiveData turns active again post-cancellation, the Flow collection is resumed.

Searching Locations: Currently, your app displays a forecast for a hardcoded location. But now by implementing coroutines and Flow you can allow the user to look for a particular location. The app performs a detailed scan for each alphabet entered by the user and updates the results simultaneously as the user types in the search box.

In the MainActivity.kt, you can create a listener and link it with the search view. As soon as the user alters the query text, the app pushes the new value forward to the query channel in Model.kt. The class Model.kt passes the text from the view to the ViewModel by using a BroadcastChannel as a bridge. The text is passed as well as the specific element is added synchronously to the channel by using offer().

Why we use flow over channels?

The Android Developers’ Summit (ADS) app was declared  as an open-source in 2019, it allows us to handle data streams with its multiple layers by exploiting flows in the best manner. The ADS app keeps each of its class concise, dedicated, reusable and testable by using a supplementary domain layer (of Use Cases) that deals with separate issues. Analogous to several high computing Android apps, the ADS app efficiently fetches data from the network classes or the cache memory; this feature can serve as a perfect use case for Flow.

Image Source: Medium

Suspend functions were a better fit for single-step operations. App refractors to use Coroutines due to two main commits that migrate to two different strategies, the first one migrate to one-shot operations while the second one migrates to data streams.

Developers are restricted to a set of principles for refactoring the app from using LiveData in all layers of the architecture. They are allowed to resort to LiveData just for establishing communication between View and ViewModel, the sub-layers of the architecture and Coroutines for the Use Case.

Image Source: Androiexample365

Try to expose streams as Flows and not Channels

There are two ways of manipulating streams of data in coroutines:

  • Flow API
  • Channel API

Channels are considered to be synchronisation primitives while Flow is created for building model streams of data, it can be considered as a warehouse for availing data stream subscriptions. Furthermore, channels are employed for supporting Flows. Flow is highly prefered by developers as it allows higher flexibility, more operators and explicit contacts in comparison to channels.

Due to the properties of terminal operators that invokes the execution of a data streams and completes them successfully or exceptionally based on the entire range of flow operations in the producer end, flows automatically locks the stream of data.

 It almost inhibits the leakage of resources at the producer end. Channels are vulnerable to such attacks, the producers might not give up the heavy resources in case the channel is not locked properly. Every app has a data layer which is responsible for allocating data by fetching it from the database or the web, it is known as the data layer.

For instance, the code snippet given below depicts a DataSource interface that exposes a stream of user event data:

interface UserEventDataSource
fun getObservableUserEvent(userId: String): Flow

Kotlin has an upper hand in Android industry not only because of its compatibility with ios but also for its advanced features such as coroutines along with Flow and LiveData, that saves a lot of developers’ time and provides a better overall user experience. By knowing the properties of Flow and the use of LiveData you can implement coroutines in a better way, by co-ordinating among the three.

ADS app was declared as an open-source so that the developers can learn the implement strategies without any abstraction and customise the default methods according to the requisites and scalability of the project on which they are working.

To read more articles on Android, click here.

By Vanshika Singolia