Hi, I am Amit Shekhar, Co-Founder @ Outcome School • IIT 2010-14 • I have taught and mentored many developers, and their efforts landed them high-paying tech jobs, helped many tech companies in solving their unique problems, and created many open-source libraries being used by top companies. I am passionate about sharing knowledge through open-source, blogs, and videos.
In this blog, we are going to learn what is Flow API in Kotlin. Kotlin provides many features out of the box that we can use to perform various tasks in our project.
When it comes to Android Development, the Flow API in Kotlin is very useful.
This article is for anyone who is curious about the Flow API in Kotlin but has no idea what it is exactly. The goal is to make you understand what is Flow API in Kotlin. If you understand what Flow API is, then my mission will be accomplished. If you read this article completely, I am sure my mission will be accomplished.
This article was originally published at Outcome School.
Let's begin.
Flow is an asynchronous data stream(which generally comes from a task) that emits values to the collector and gets completed with or without an exception.
This will make more sense when we go through the example. Let's take a standard example of image downloading.
Assume that we have a task: To download an image, emit the items(values) which are the percentage of the image downloading like 1%, 2%, 3%, and so on. It can get completed with or without an exception. If everything goes well, the task will be completed without an exception. But, in case of network failure, the task will be completed with an exception.
So, there will be a task that will be done and will emit some values which will be collected by the collector.
Now, let's discuss the major components of Flow.
The major components of Flow are as below:
- Flow Builder
- Operator
- Collector
Let's understand this with the following analogy.
Flow Builder | -> | Speaker |
Operator | -> | Translator |
Collector | -> | Listener |
Flow Builder
In simple words, we can say that it helps in doing a task and emitting items. Sometimes it is just required to emit the items without doing any task, for example, just emit a few numbers (1, 2, 3). Here, the flow builder helps us in doing so. We can think of this as a Speaker. The Speaker will think(do a task) and speak(emit items).
Operator
The operator helps in transforming the data from one format to another.
We can think of the operator as a Translator. Assume that the Speaker is speaking in French and the Collector(Listener) understands English only. So, there has to be a translator to translate French into English. That translator is an Operator.
Operators are more than this actually, using the operator, we can also provide the thread on which the task will be done. We will see this later.
Collector
The collector collects the items emitted using the Flow Builder which are transformed by the operators.
We can think of a collector as a Listener. Actually, Collector also comes under the operator which is known as Terminal Operator. The collector is a Terminal Operator. For now, we will skip the Terminal Operator as that is not needed for this blog on Flow API.
Flow API Source Code
The Flow interfaces look like the below in the source code of Coroutines:
public fun interface FlowCollector<in T> {
public suspend fun emit(value: T)
}
public interface Flow<out T> {
public suspend fun collect(collector: FlowCollector<T>)
}
Hello World of Flow
flow {
(0..10).forEach {
emit(it)
}
}.map {
it * it
}.collect {
Log.d(TAG, it.toString())
}
flow { } |
-> | Flow Builder |
map { } |
-> | Operator |
collect {} |
-> | Collector |
Let's go through the code.
- First, we have a flow builder which is emitting 0 to 10.
- Then, we have a map operator which will take each and every value and square(it * it). The map is Intermediate Operator.
- Then, we have a collector in which we get the emitted values and print them as 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100.
Note: When we actually connect both the Flow Builder and the Collector using the collect method, then only, it will start executing.
Now it's time to learn more about the Flow Builder.
Types of flow builders
There are 4 types of flow builders:
-
flowOf()
: It is used to create flow from a given set of items. -
asFlow()
: It is an extension function that helps to convert type into flows. -
flow{}
: This is what we have used in the Hello World example of Flow. -
channelFlow{}
: This builder creates flow with the elements using send provided by the builder itself.
Examples:
flowOf()
flowOf(4, 2, 5, 1, 7)
.collect {
Log.d(TAG, it.toString())
}
asFlow()
(1..5).asFlow()
.collect {
Log.d(TAG, it.toString())
}
flow{}
flow {
(0..10).forEach {
emit(it)
}
}
.collect {
Log.d(TAG, it.toString())
}
channelFlow{}
channelFlow {
(0..10).forEach {
send(it)
}
}
.collect {
Log.d(TAG, it.toString())
}
At the end of this article, we will also learn to create Flow using Flow Builder. Now we need to learn about the flowOn
operator.
flowOn
Operator
flowOn
Operator is very handy when it comes to controlling the thread on which the task will be done.
Usually, in Android, we do a task on a background thread and show the result on the UI thread.
Let's see this with an example: We have added a delay of 500 milliseconds inside the flow builder to simulate delay.
val flow = flow {
// Run on Background Thread (Dispatchers.Default)
(0..10).forEach {
// emit items with 500 milliseconds delay
delay(500)
emit(it)
}
}
.flowOn(Dispatchers.Default)
CoroutineScope(Dispatchers.Main).launch {
flow.collect {
// Run on Main Thread (Dispatchers.Main)
Log.d(TAG, it.toString())
}
}
Here the task inside the flow builder will be done on the background thread which is Dispatchers.Default
.
Now, we need to switch it to the UI thread. To achieve that, we need to wrap our collect API inside the launch with Dispatchers.Main
.
This is how the flowOn
operator can be used to control the thread.
flowOn()
is likesubscribeOn()
in RxJava
Dispatchers: They help in deciding the thread on which the work has to be done. There are majorly three types of Dispatchers which are IO, Default, and Main. IO dispatcher is used for network and disk-related tasks. Default is used for CPU-intensive work. The Main is the UI thread of Android.
Now, we will learn how to create our Flow using the Flow builder. We can create our Flow for any task using the Flow Builder.
Creating Flow Using Flow Builder
Let's learn it through examples.
1. Move File from one location to another location
Here, we will create our Flow using the Flow Builder for moving the file from one location to another in the background thread and send the completion status on Main Thread.
val moveFileflow = flow {
// move file on background thread
FileUtils.move(source, destination)
emit("Done")
}
.flowOn(Dispatcher.Default)
CoroutineScope(Dispatchers.Main).launch {
moveFileflow.collect {
// when it is done
}
}
2. Downloading an Image
Here, we will create our Flow using the Flow Builder for downloading the Image which will download the Image in the background thread and keep sending the progress to the collector on the Main thread.
val downloadImageflow = flow {
// start downloading
// send progress
emit(10)
// downloading...
// ......
// send progress
emit(75)
// downloading...
// ......
// send progress
emit(100)
}
.flowOn(Dispatcher.Default)
CoroutineScope(Dispatchers.Main).launch {
downloadImageflow.collect {
// we will get the progress here
}
}
This is how we can create our Flow.
In Kotlin, Coroutine is just the scheduler part of RxJava but now with Flow API coming alongside it, it can be an alternative to RxJava in Android
So, now we have a good knowledge of Flow API in Kotlin. We have understood what exactly is Flow API in Kotlin.
Now, you can start using the Flow API in your Android project.
That's it for now.
Thanks
Amit Shekhar
Co-Founder @ Outcome School
You can connect with me on:
Top comments (0)