Mastering Flow API in Kotlin

Authors
  • Amit Shekhar
    Name
    Amit Shekhar
    Published on
Mastering Flow API in Kotlin

I am Amit Shekhar, 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.

Join my program and get high paying tech job: amitshekhar.me

Before we start, I would like to mention that, I have released a video playlist to help you crack the Android Interview: Check out Android Interview Questions and Answers.

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 blog is a part of the series I have written on Flow API in Kotlin:

Let's begin with the 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.

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:

  1. flowOf(): It is used to create flow from a given set of items.
  2. asFlow(): It is an extension function that helps to convert type into flows.
  3. flow{}: This is what we have used in the Hello World example of Flow.
  4. 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 like subscribeOn() 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(Dispatchers.IO)
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(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

So, now we have a good knowledge of Flow API in Kotlin. We have understood what exactly is Flow API in Kotlin.

Learn Kotlin Flow by real examples for Android from here.

Now, you can start using the Flow API in your Android project.

Prepare yourself for Android Interview: Android Interview Questions

That's it for now.

Thanks

Amit Shekhar

You can connect with me on:

Read all of my high-quality blogs here.