Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, we will learn how Flow APIs work in Kotlin and how can we start using it in our android projects.
If we are working as an Android developer and looking to build an app asynchronously we might be using RxJava as it has an operator for almost everything. RxJava has become one of the most important things to know in Android.
But with Kotlin a lot of people tend to use Co-routines. With Kotlin Coroutine 1.2.0 alpha release Jetbrains came up with Flow API as part of it. With Flow in Kotlin now you can handle a stream of data that emits values sequentially.
“In Kotlin, Coroutine is just the scheduler part of RxJava but now with Flow APIs coming along side it, it can be alternative to RxJava in Android.”
We will cover the following topics to understanding the Flow API :
- What is Flow APIs in Kotlin Coroutines?
- Start Integrating Flow APIs in your project
- Builders in Flows
- Few examples using Flow Operators.
A famous quote about learning is :
“Learn as though you would never be able to master it; hold it as though you would be in fear of losing it.”
So Let’s begin.
What is Flow APIs in Kotlin Coroutines?
Flow API in Kotlin is a better way to handle the stream of data asynchronously that executes sequentially.
So, in RxJava, Observables type is an example of a structure that represents a stream of items. Its body does not get executed until it is subscribed to by a subscriber and once it is subscribed, subscriber starts getting the data items emitted. Similarly, Flow works on the same condition where the code inside a flow builder does not run until the flow is collected.
Start Integrating Flow APIs in your Project
Let us create an android project and then let’s start integrating the Kotlin Flow APIs.
Step 01
Add the following in the app’s build.gradle,
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
and in the project’s build.gradle add,
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"
Step 02
In MainActivity’s layout file let’s create a UI that will have a button.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:text="Launch Kotlin Flow"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Step 03
Now, let’s begin the implementation of Flow APIs in MainActivity. In onCreate() function of Activity lets add two function like,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupFlow()
setupClicks()
}
Here, setupFlow() is the function where we will define the flow and setupClicks() is the function where we will click the button to display the data which is emitted from the flow.
We will declare a lateinit variable of Flow of Int type,
lateinit var flow: Flow<Int>
Step 04
Now, in setupFlow() I will emit items after 500milliseconds delay.
fun setupFlow(){
flow = flow {
Log.d(TAG, "Start flow")
(0..10).forEach {
// Emit items with 500 milliseconds delay
delay(500)
Log.d(TAG, "Emitting $it")
emit(it)
}
}.flowOn(Dispatchers.Default)
}
Here,
- We will emit numbers from 0 to 10 at 500ms delay.
- To emit the number we will use emit() which collects the value emitted. It is part of FlowCollector which can be used as a receiver.
- and, at last, we use flowOn operator which means that shall be used to change the context of the flow emission. Here, we can use different Dispatchers like IO, Default, etc.
” flowOn() is like subscribeOn() in RxJava.”
Step 05
Now, we need to write setupClicks() function where we need to print the values which we will emit from the flow.
private fun setupClicks() {
button.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
flow.collect {
Log.d(TAG, it.toString())
}
}
}
}
When we click the button we will print the values one by one.
Here,
flow.collect now will start extracting/collection the value from the flow on the Main thread as Dispatchers.Main is used in launch coroutine builder in CoroutineScope. The output which will be printed in Logcat is,
D/MainActivity: Start flow
D/MainActivity: Emitting 0
D/MainActivity: 0
D/MainActivity: Emitting 1
D/MainActivity: 1
D/MainActivity: Emitting 2
D/MainActivity: 2
D/MainActivity: Emitting 3
D/MainActivity: 3
D/MainActivity: Emitting 4
D/MainActivity: 4
D/MainActivity: Emitting 5
D/MainActivity: 5
D/MainActivity: Emitting 6
D/MainActivity: 6
D/MainActivity: Emitting 7
D/MainActivity: 7
D/MainActivity: Emitting 8
D/MainActivity: 8
D/MainActivity: Emitting 9
D/MainActivity: 9
D/MainActivity: Emitting 10
D/MainActivity: 10
As we can see Flow starts only when the button is clicked as it prints Start Flow and then starts emitting. This is what we meant by Flows are cold.
Let’s say we update the setupFlow() function like,
private fun setupFlow() {
flow = flow {
Log.d(TAG, "Start flow")
(0..10).forEach {
// Emit items with 500 milliseconds delay
delay(500)
Log.d(TAG, "Emitting $it")
emit(it)
}
}.map {
it * it
}.flowOn(Dispatchers.Default)
}
Here we can see that we added map operator which will take each and every item will square itself and print the value.
Anything, written above flowOn will run in background thread.
Builders in Flow
Flow builders are nothing but the way to build Flows. There are 4 types of flow builders:
- flowOf() – It is used to create flow from a given set of values. For Example:
flowOf(4, 2, 5, 1, 7).onEach { delay(400) }.flowOn(Dispatcher.Default)
Here, flowOf() takes fixed values and prints each of them after a delay of 400ms. When we attach a collector to the flow, we get the output:
D/MainActivity: 4
D/MainActivity: 2
D/MainActivity: 5
D/MainActivity: 1
D/MainActivity: 7
2. asFlow() – It is an extension function that helps to convert type into flows. For Example:
(1..5).asFlow().onEach{ delay(300)}.flowOn(Dispatchers.Default)
Here, we converted a range of values from 1 to 5 as flow and emitted each of them at a delay of 300ms. When we attach a collector to the flow, we get the output,
D/MainActivity: 1
D/MainActivity: 2
D/MainActivity: 3
D/MainActivity: 4
D/MainActivity: 5
The output is the number being printed from 1 to 5, whichever were present in the range.
3. flow{} – This example has been explained in the Android Example above. This is a builder function to construct arbitrary flows.
4. channelFlow{} – This builder creates cold-flow with the elements using send provided by the builder itself. For Example,
channelFlow {
(0..10).forEach {
send(it)
}
}.flowOn(Dispatchers.Default)
This will print,
D/MainActivity: 0
D/MainActivity: 1
D/MainActivity: 2
D/MainActivity: 3
D/MainActivity: 4
D/MainActivity: 5
D/MainActivity: 6
D/MainActivity: 7
D/MainActivity: 8
D/MainActivity: 9
D/MainActivity: 10
Now, finally, let’s discuss how to use an operator of Flow in our project.
Few examples using Flow Operators
Zip Operator
In above Android Example, we have two functions setupFlow() and setupClicks(). We will modify both functions in MainActivity.
First, we will declare two lateinit variables of Flow of String type,
lateinit var flowOne: Flow<String>
lateinit var flowTwo: Flow<String>
Now, the two functions setupFlow() and setupClicks() will be called in onCreate() of MainActivity.
override fun onCreate(savedInstanceState: Bundle?) {
...
setupFlow()
setupClicks()
}
and in the setupFlow(), we will initialize two flows to the two variables,
private fun setupFlow() {
flowOne = flowOf("1", "2", "3").flowOn(Dispatchers.Default)
flowTwo = flowOf("4", "5", "6").flowOn(Dispatchers.Default)
}
and in setupClicks(), we will zip both the flows using zip operator,
private fun setupClicks() {
button.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
flowOne.zip(flowTwo)
{ firstString, secondString ->
"$firstString $secondString"
}.collect {
Log.d(TAG, it)
}
}
}
}
Here,
- when the button is clicked the scope is launched.
- flowOne is zipped with flowTwo to give a pair of values that we have created a string and,
- then we collect it and print it in Logcat. The resulting output will be,
D/MainActivity: 14
D/MainActivity: 25
D/MainActivity: 36
If both flows doesn’t have the same number of item, then the flow will stop as soon as one of the flow completes.
That’s all about in this article.
Conclusion
In this article, we learned about how Flow APIs work in Kotlin and how can we start using it in our android projects. We have also discussed builders in flow and zip operators.
Thanks for reading ! I hope you enjoyed and learned about Flow API concepts in Kotlin. Reading is one thing, but the only way to master it is to do it yourself.
Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Thanks again Reading. HAPPY READING !!???