Reactive Programming in Kotlin - SharedFlow
Now that Flow
has been covered, we turn to its child SharedFlow
. This new type opens up even more possibilities with programming in a functional reactive style. While a lot of what was covered in the previous article will carry forward to this article, SharedFlow
has several key differences when compared to Flows.
Hot Flow #
The first and most important difference is that a SharedFlow
is hot instead of cold. This means that even if there is no collection happening on the SharedFlow, values are still being emitted by the Flow. This also means that when a SharedFlow
is subscribed to, the active instance will be returned. This allows multiple subscribers to receive values from the same underlying Flow. This is unlike a cold Flow which creates a new instance each time it is subscribed to.
Sending Values #
Unlike a Flow
which generates its data internally, a SharedFlow
(specifically a MutableSharedFlow
) is written to externally. This is done using the suspending emit
function or the non-suspending tryEmit
function. The suspending version will suspend until the value is added to the buffer, but the non-suspending version will instead attempt to add the value to the buffer and return false if unable.
Replay and Buffer #
With a hot flow, there is a need to allow values to be sent to new subscribers so they don't have to wait until a new value arrives. This is done using a replay cache that re-emits the last X values to new subscribers. By default this value is 0, the same as a Rendezvous Channel.
Due to the nature of multiple subscribers, some may consume emissions faster than others. So as not to block all subscribers, a SharedFlow
has a buffer. By default this buffer is the size of the replay cache but can be extended with the extraBufferCapacity
parameter. Additionally, a buffer overflow policy can be set. By default the overflow will cause the sender/emitter to suspend, but other policies may be chosen to prevent suspending and instead drop values from the buffer.
Using a SharedFlow #
To create a SharedFlow
, create a MutableSharedFlow
and convert to a SharedFlow
using asSharedFlow()
or use the shareIn()
to convert a cold Flow
into a hot SharedFlow
.
val mutableSharedFlow = MutableSharedFlow<Int>()
val sharedFlow = mutableSharedFlow.asSharedFlow()
val myFlow = flowOf(1,2,3)
val flowSharedFlow = myFlow.shareIn(
scope = coroutineScope,
started = SharingStarted.Lazily
replay = 3, // Defaults to 0
)
To collect a SharedFlow
, apply a terminal operator just like on a Flow
.
val sharedFlow = MutableSharedFlow<Int>().asSharedFlow()
val subscriber1 = sharedFlow.collect { ... }
val subscriber2 = sharedFlow.first()
Finally, to send values to a MutableStateFlow
, use either emit
or tryEmit
.
val sharedFlow = MutableSharedFlow<Int>()
coroutineScope.launch {
sharedFlow.emit(1)
}
sharedFlow.tryEmit(2)
Conclusion #
Two of the three Flow types have been covered. Flow
the single subscriber cold stream of data and SharedFlow
the multiple subscriber hot stream of data. Next week an even more specialized child of SharedFlow
called StateFlow
will be covered to wrap up this series. Until next time, thanks!
Did you find this content helpful?
Please share this post and be sure to subscribe to the RSS feed to be notified of all future articles!
Want to go above and beyond? Help me out by sending me $1 on Ko-fi. It goes a long way in helping run this site and keeping it advertisement free. Thank you in advance!