DEV Community

Cover image for Introduction to Kotlin Flows and Channels
Vincent Tsen
Vincent Tsen

Posted on • Updated on • Originally published at vtsen.hashnode.dev

Introduction to Kotlin Flows and Channels

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

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

Behaviors Cold Stream Hot Stream
Where data is produced? Inside the stream outside the stream
Unicast or multicast? Unicast Multicast
Lazy or eager stream? Lazy stream Eager 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)
    }
}

Enter fullscreen mode Exit fullscreen mode

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 */
    } 
}
Enter fullscreen mode Exit fullscreen mode

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>()
Enter fullscreen mode Exit fullscreen mode

Send Data into the Hot Stream

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

Enter fullscreen mode Exit fullscreen mode

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()
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 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 use 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 usage 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)


Originally published at https://vtsen.hashnode.dev.

Top comments (0)