Kotlin Flow is a new asynchronous stream library from JetBrains, it is the same
company behind the Kotlin language. This shares many similarities with Rx streams. In this post we introduce Flow, we talk about its need, advantages and use cases.
What’s common in a sports app, weather app, stock market app or an app which keeps track of the user’s real time location? All these apps have some data point which might change within seconds. Sports app has scorecard, stock market app has market Index which will keep fluctuating and similarly others too have some real-time data points. This is where Flow helps — helps to handle data stream asynchronously.
Why am I here?
In this post, we introduce Flow. We will talk about its need, advantages and use cases. We will also introduce a couple of problem statements which we want to solve, solutions for problems would be part of the next post in this series.
Let me start by defining the problem statements first. Eventually, that’s what we want to do — solve practical problems.
Problem 1: How to do Polling in Android?
As you see, this is a screenshot from Twitter app. This has a tweet and other relevant details. If you look at the likes and retweets count number, they get updated in real time without the need for the user to refresh the page. We can think of it as a stream of data which the page is receiving and as soon as there is an update, app displays the updated result.
Problem 2: Implement Instant search
Every other app has this use case of displaying search results instantly. Again, we can think of user input as a stream of data which makes this problem, a standard use case for Flow.
I have already solved both problems. Why do I need a new solution today?
Let’s talk about solution in-hand to solve problem 1. In the end, it comes to implement polling or an implementation similar to socket connection. Socket connection is not in the scope of this post so let’s talk about polling. To implement polling, one of the solutions is to use Handler. Handler will take a runnable as a param which will run the intended task (i.e. API call) and every time this task is run, in the runnable we can call handler.postDelayed which will trigger the task after every x seconds.
Now, this looks nice and simple but, in the process, we have to take care of a few things such as —
- Register and unregister handler as per lifecycle of the components.
- Start/stop polling manually if needed.
- Passing data from one thread to another thread.
- To solve the 3) problem, I used LiveData but then I have to pass around an instance of LiveData in my custom poller which was not looking very clean.
To overcome some of these challenges along with asynchronous programming paradigm Flow is introduced in Kotlin.
What is Flow?
This’s what they say — “Flow is a cold asynchronous data stream that sequentially emits values and completes normally or with an exception”. A cold stream is a data source whose producer will execute for listener only once they start consuming it. If there are no consumers, stream of data will be closed automatically.
If we talk in respect of our twitter example -> Data stream would be the result of API which fetches likes/retweets count in regular interval, this will act as a producer. Our UI can be the consumer of this data. As long as UI is alive, it will keep listening to the result produced by the data source (API). Once the UI or the component is not present, automatically API would stop fetching the data since there would not be any consumer of the stream.
How does flow help to overcome the challenges presented by data stream asynchronous programming?
Easy to write asynchronous code
Flow is built on top of coroutines. we can write asynchronous code the same way we write synchronous code using Coroutines. They make it easy to switch threads and pass data with one another.
Coroutines is preferred choice because of their lighter and super-fast execution. Also, Coroutine occupies only a few dozen bytes of heap memory compared to a thread which has its own stack typically 1MB in size.
Life-cycle aware handling & Structured concurrency
Flow is designed to handle data stream in a reactive way (similar to LiveData) so as soon as the subscriber is disconnected, Flow will stop producing new data.
Let’s say we are listening for a flow object (emitted by data source in regular interval) in our UI layer. Now UI gets killed because of an operation performed by the user. As it happens, data source will stop emitting the Flow object and hence will stop the operation performed by data source.
In addition, Coroutines provides structured concurrency so flow supports the same. Coroutines run within scope and as soon as scope is eliminated, coroutine would stop.
Activity, fragment and ViewModel has their predefined scope which can be accessed using lifecycleScope and viewmodelScope respectively. Once we launch a coroutine using these scopes, coroutine would be bound to lifecycle of the component and as soon as the scope is destroyed or eliminated, coroutine will be cancelled automatically.
Similar to scope, there are coroutine extensions method available which are linked to lifecycle of the component. i.e. launchWhenCreated, launchWhenStarted etc.
Suggested App architecture
Points to note:
- ViewModel and repository layer is observing a flow object.
MVVM Architecture & LiveData, ViewModel, LifeCycle Components
MVC, MVP and MVVM are some of the Architecture patterns used by developers around the world. Which one to use, depends…
2. View is observing a LiveData object. LiveData is an observable data holder that is also lifecycle aware. It helps with problems or issues which circles around lifecycle of a component. Additionally, it can be updated from any thread. This brings the next question —
Why do we need Flow? Can’t we use either Flow or LiveData or Do we need both?
Why Flow over LiveData
- LiveData was never designed to be fully-fledged reactive control. It’s good for one shot operation where we get all the data in one shot. But if you are getting a data stream or you are getting data in multiple packets, flow would be the right choice.
- In case of LiveData, if you want to perform certain transformation, by default they would run on main thread. so if you have to dispatch the transformation on different thread, you probably would want to use suspend functions. Additionally, LiveData has a very small set of built-in operators while flow comes up with lots of them.
Why LiveData over Flow
- LiveData caches the result. Let’s say, you fetch a result and it is stored in a LiveData object. Now user rotates the device to landscape mode, the activity will be recreated. Since the result was already fetched while device was in portrait mode, LiveData would emit the same result again so we would not need to refetch the results. In case of flow, it does not cache the result so in the same scenario we have to refetch the results, which is why it’s suggested to use LiveData between the view and view model layer.
- LiveData takes care of data binding which means every-time there is data change, it can be observed in the view XML itself directly and the object does not need to observed in UI class of component. This is a kind of add-on in LiveData.
If you have a use case where you are not worried about LiveData caching, you can achieve same thing using flow.
So far we talked about
- The problems which we want to solve.
- What is Flow and its advantage over current ways.
- Suggested App architecture.
Next in the line
I will take a pause here. I hope, I have given you a reason to try Flow. You can try stable version of Flow API starting with Kotlin 1.3.0. Going forward in the next post, we will jump to the solution for both of our problems. We will also talk about some of the operators which come with Flow.
Lastly, Thank you for reading the article. Any questions and suggestions are most welcome. Happy Coding 😃