Introduction to Kotlin Flows and Channels

Summarizing the behaviors of using Kotlin flows (cold stream) and Kotlin channels (hot stream)

Introduction to Kotlin Flows and Channels

This is part of the asynchronous flow series:

Kotlin Flow is a cold stream, Kotlin Channel is a hot stream. The differences between hot and cold streams are summarized below.

Cold Stream vs Hot Stream

BehaviorsCold StreamHot Stream
Where data is produced?Inside the streamoutside the stream
Unicast or multicast?UnicastMulticast
Lazy or eager stream?Lazy streamEager stream

Terminology

  • Unicast - has only one subscriber per stream, new subscription creates new stream

  • Multicast - has no or many subscribers per stream, new subscription uses existing stream

  • Lazy stream - start emitting values only when someone start subscribing to it

  • Eager stream - start emitting values even it does NOT have any subscribers

Kotlin Flows

Kotlin_Flows.drawio.png

Flow data is produced inside the stream by creating the Flow and calling the FlowCollector<T>.emit(). It is called upstream flow.

val flow: Flow<Int> = flow {
    repeat(10000) { value ->
        delay(1000)
        emit(value)
    }
}

However, the data won't be emitted until the Flow<T>.collect() is called. This is an example of collecting flow in ViewModel. It is called downstream flow.

viewModelScope.launch {  
    flow.collect {  value ->
        /* collecting $value here */
    } 
}

Each time the Flow<T>collect() is called, a new cold stream is created. It is called unicast. Data is only emitting at this point - lazy stream.

Kotlin Channels

Kotlin_Channels.drawio.png

Channel data is produced outside the stream. Creating the channel and sending the data into the hot stream are separated. Sending data to the hot stream is called channel's sender.

Create the Channel

private val channel = Channel<Int>()

Send Data into the Hot Stream

viewModelScope.launch {
    repeat(10000) { value ->
        delay(1000)
        channel.send(value)
    }
}

Hot stream is like a buffer, which has the buffer capacity. The default buffer capacity is zero. By default, when the buffer is overflow, the data sending is suspended until there is a receiver.

To receive the channel’s data, you call the Channel.receive(). This is called the channel's receiver.

viewModelScope.launch {
    while(true) {
        val value = channel.receive()
    }
}
  • Since the buffer capacity is zero by default, the buffer overflow happens as soon as you're sending the first data into the stream. Thus, the data sending is suspended after the first data until there is a receiver. This behavior looks very similar to lazy stream, but it is NOT. It is still an eager stream.

  • Imagine if the buffer capacity is more than zero, sending data occurs even without any receiver until the buffer is full. Thus, it is an eager stream.

Unlike Flow, having a new subscriber or new channel's receiver does not create a new stream. It still uses the existing stream. Thus, it is multicast stream.

One thing I would like to emphasize is once the data is received, the data is removed from the hot stream / buffer.

Kotlin_Channels_2.drawio.png

Based on the diagram above, assuming there are already channel's receiver #1, #2 and #3 subscribing to the channel (calling the Channel.receive()function), the channel's sender is sending 1, 2 and 3 to the hot stream.

  • Once 1 is received by receiver #1, receiver #2 and #3 won't receive 1 anymore.

  • Receiver #2 receives 2 instead, and receiver #3 receives 3.

Conclusion

I just summarize the behavior of using Flow and Channel at high-level. I do not really know any practical usages of them, especially Channel.

These are the notes that I have written down:

  • Flow - store single value that changes over time

  • Channel - store one-time event, collected by one observer?

Flow probably makes sense. I have been using it to expose the data from a local database. But honestly, when do we use Channel? Snackbar? If you have a good practical use of it, please share that with me.

[Updated - Sept 23, 2022]: After doing some research and found the following article by Google team which does NOT recommend Channel for a one-time event. In short, use everything with StateFlow - will be covered in the next article.

Source Code

GitHub Repository: Demo_AsyncFlow (see FlowActivity and ChannelActivity)

Did you find this article valuable?

Support Vincent Tsen - AndroidDev Blog by becoming a sponsor. Any amount is appreciated!