Flow API In Kotlin Flashcards
What is Flow API In Kotlin ?
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.
What is 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
Descride the Major components details of Kotlin Flow.
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.
Hello World Example of Flow
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.
Explain the Types of flow builders.
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.
Explain Flow Builders Examples.
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())
}
Examples of Creating Flow Using Flow Builder.
Let’s learn it through examples.
- 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(Dispatchers.IO)
CoroutineScope(Dispatchers.Main).launch {
moveFileflow.collect {
// when it is done
}
}
- 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(Dispatchers.IO)
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
Cold Flow vs Hot Flow
**Cold Flow **
- It emits data only when there is a collector.
- It does not store data.
- It can’t have multiple collectors.
Hot Flow
* It emits data even when there is no collector.
* It can store data.
* It can have multiple collectors.
In Cold Flow, in the case of multiple collectors, the complete flow will begin from the beginning for each one of the collectors, do the task and emit the values to their corresponding collectors. It’s like 1-to-1 mapping. 1 Flow for 1 Collector. It means a cold flow can’t have multiple collectors as it will create a new flow for each of the collectors.
In Hot Flow, in the case of multiple collectors, the flow will keep on emitting the values, collectors get the values from where they have started collecting. It’s like 1-to-N mapping. 1 Flow for N Collectors. It means a hot flow can have multiple collectors.
StateFlow vs SharedFlow
StateFlow
1. Hot Flow
2. Needs an initial value and emits it as soon as the collector starts collecting.
3. val stateFlow = MutableStateFlow(0)
4. Only emits the last known value.
5. It has the value property, we can check the current value. It keeps a history of one value that we can get directly without collecting.
6. It does not emit consecutive repeated values. It emits the value only when it is distinct from the previous item.
7. Similar to LiveData except for the Lifecycle awareness of the Android component. We should use repeatOnLifecycle scope with StateFlow to add the Lifecycle awareness to it, then it will become exactly like LiveData.
SharedFlow
1. Hot Flow
2. Does not need an initial value so does not emit any value by default.
3. val sharedFlow = MutableSharedFlow<Int>()
4. Can be configured to emit many previous values using the replay operator.
5. It does not have the value property.
6. It emits all the values and does not care about the distinct from the previous item. It emits consecutive repeated values also.
7.Not similar to LiveData.</Int>
StateFlow Example
Let’s understand all of the above points from the example code.
StateFlow example
Suppose we have StateFlow as below:
val stateFlow = MutableStateFlow(0)
And, we start collecting on it:
stateFlow.collect {
println(it)
}
As soon as we start collecting, we will get:
0
We get this “0” as it takes an initial value and emits it immediately.
And then, if we keep on setting the values as below:
stateFlow.value = 1
stateFlow.value = 2
stateFlow.value = 2
stateFlow.value = 1
stateFlow.value = 3
we will get the following output:
1
2
1
3
Notice here that we are getting “2” only once, not twice. As it does not emit consecutive repeated values.
Suppose we add a new collector now:
stateFlow.collect {
println(it)
}
We will get:
3
As the StateFlow stores the last value and emits it as soon as a new collector starts collecting.
SharedFlow example
SharedFlow example
Suppose we have SharedFlow as below:
val sharedFlow = MutableSharedFlow<Int>()</Int>
And, we start collecting on it:
sharedFlow.collect {
println(it)
}
As soon as we start collecting, we will not get anything as it does not take an initial value.
And then, if we keep on emitting the values as below:
sharedFlow.emit(1)
sharedFlow.emit(2)
sharedFlow.emit(2)
sharedFlow.emit(1)
sharedFlow.emit(3)
we will get the following output:
1
2
2
1
3
Notice here that we are getting “2” twice. As it emits consecutive repeated values also.
Suppose we add a new collector now:
sharedFlow.collect {
println(it)
}
We will not get anything as the SharedFlow does not store the last value.