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:
Part 2 - Introduction to Kotlin Flows and Channels
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
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
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.
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 receive1
anymore.Receiver #2 receives
2
instead, and receiver #3 receives3
.
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 timeChannel
- 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 withStateFlow
- will be covered in the next article.
Source Code
GitHub Repository: Demo_AsyncFlow (see FlowActivity
and ChannelActivity
)