Skip to main content
Donovan LaDuke - Developer

Reactive Programming in Kotlin - SharedFlow


Featured in Android Weekly Issue 597

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!

Buy me a Coffee on Ko-fi