Kotlin Coroutines and Flows: A Beginner's Guide to Asynchronous Programming

Based in India.
Search for a command to run...

Based in India.
No comments yet. Be the first to comment.
I recently had to deploy a Ktor project to Railway for my hobby project. I'll share the how-to through this article. Introduction Ktor is a framework that allows us to build web applications in Kotlin, built and backed by Jetbrains. It's open-source,...

The problem When working with JVM (Java Virtual Machine), the Garbage Collector will take care of allocating and deallocating memory for us - Most of the time. In some cases, it fails to release the memory for some objects which are no longer in use,...

What are quick actions? Quick actions under the hood are App Shortcuts(in Android) and Home Screen Actions(in iOS) which allow users to perform certain app actions without having to open the app. Where have I seen this? You might have already seen an...

What are app shortcuts? App shortcuts (introduced in Android Nougat 7.1) allow users to perform certain app actions without having to open the app. Developers can allow users to pin certain app actions to home screen. Let's see how. Where have I seen...

Kotlin Coroutines and Flows are a powerful tool for managing asynchronous programming in your Android apps. This article is a beginner's guide to understanding Kotlin Flow.
First, let's understand what coroutines are. Coroutines are a lightweight alternative to traditional threads that allow developers to perform background tasks without the need for explicit thread management, making your code more readable and less error-prone. They are much cheaper than threads in terms of memory usage, which is especially important when working with mobile devices.
Flows are a new addition to the Kotlin coroutines library. They provide a way to handle streams of data in a reactive and composable manner. This means we can use flows to handle multiple asynchronous events and perform operations on the resulting data streams. Flows also allow for easy error handling and cancellation, which can greatly simplify our code.
To get started with coroutines and flows, you'll need to add the following dependencies to your app's build.gradle file:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:X.Y.Z'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:X.Y.Z'
You can get the latest version of the dependency from the official website, here.
Let's understand the emitting and collection of Flow -
When a flow is created, it can produce a stream of data, the emit function is used to send that data to the stream.
For example, a flow that generates a stream of integers could use the emit function to send each integer to the stream.
The collect function is used to receive data from a flow. It allows you to act on each value emitted by the flow.
flow{}
flow {
for (i in 1..10) {
emit(i)
}
}
.asFlow()
(1..10).asFlow()
flowOf()
flowOf(1, 2, "Three", "Four")
fun flowExample(): Flow<Int> {
return flow {
for (i in 1..10) {
emit(i)
delay(1000L)
}
}
}
fun main() {
println("Flow started")
runBlocking {
flowExample().collect {
println("$it at ${System.currentTimeMillis()}")
}
}
println("Flow ended")
}
Output:
Flow started
1 at 1673531247787
2 at 1673531248791
3 at 1673531249792
4 at 1673531250797
5 at 1673531251803
6 at 1673531252807
7 at 1673531253809
8 at 1673531254811
9 at 1673531255815
10 at 1673531256816
Flow ended
In the above example, we are emitting numbers 1 to 10 with a delay of 1 second between in number along with the time in milliseconds. We can also perform various operations on the flow before emitting it.
Let's see a few examples of the operators provided by the library -
map
This will return a flow containing the results of applying the given transform function to each value of the original flow -
fun flowExample(): Flow<Int> {
return (1..10).asFlow().map { it * 2 }
}
filter
This will return a flow containing only values of the original flow that matches the given predicate -
fun flowExample(): Flow<Int> {
return (1..10).asFlow()
.filter { it % 2 == 0 }
}
transform
This will apply transform function to each value of the given flow -
fun flowExample(): Flow<Int> {
return (1..10).asFlow()
.transform { value ->
if (value % 2 == 0) {
emit(value)
delay(1000L)
emit(value)
}
}
}
onEach
This will return a flow that performs the given action on each value of the original flow -
fun flowExample(): Flow<Int> {
return (1..10).asFlow()
.onEach { println(it * 2) }
}
That's all for the article, I hope you found this article useful!
Follow me for more articles like this. Have a great day! 😊