StateFlow and SharedFlow
The Kotlin Flow library provides a component supporting reactive and asynchronous programming. Within this library, there are two significant types of flows, namely StateFlow and SharedFlow. In this article, we will concentrate on the fundamental differences between StateFlow and SharedFlow and discuss scenarios for the use of each.
What is the StateFlow?
StateFlow is a type of flow that represents the current state of a stream and can broadcast this state. In other words, StateFlow tracks the moment when a value changes and conveys the updated value to all subscribers observing it. StateFlow is commonly used, particularly for managing situations like UI (user interface) updates.
class ExampleViewModel : ViewModel() {
private val _currentState = MutableStateFlow("Initial State")
val currentState: StateFlow<String> = _currentState
fun updateState(newState: String) {
_currentState.value = newState
}
}
In the example above, the StateFlowViewModel
class contains a private MutableStateFlow
named _currentState
. This is exposed externally through a StateFlow
named currentState
. The updateState
function updates the current state.
StateFlow and LiveData
StateFlow and LiveData are similar constructs, both featuring observable data with different behaviors:
- StateFlow requires an initial value, unlike LiveData.
- The observe function in LiveData automatically cancels listening when the lifecycle transitions to the STOPPED state and resumes when it transitions to the STARTED state. However, the collect function in StateFlow does not automatically stop; to achieve similar behavior, the flow must be collected within a Lifecycle.repeatOnLifecycle block.
val flow = flow {
(1..3).forEach {
emit(it)
}
}
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
flow.collect{
println(it)
}
}
}
In the example above, the flow is collected only when lifecycle is in the STARTED state.
What is the SharedFlow?
SharedFlow is a type of flow that allows multiple subscribers to share the same values. Unlike StateFlow, when one subscriber receives a value, other subscribers can also receive that value. This enables updates to be instantly received by multiple subscribers from different locations.
class SharedFlowExample {
private val _sharedFlow = MutableSharedFlow<String>()
val sharedFlow: SharedFlow<String> = _sharedFlow
fun emitValue(value: String) {
viewModelScope.launch {
_sharedFlow.emit(value)
}
}
}
In the example above, the SharedFlowExample
class contains a private MutableSharedFlow
named _sharedFlow
, and exposes this flow through a SharedFlow
named sharedFlow
. The emitValue
function adds a new value to the shared flow.
StateFlow vs SharedFlow
While StateFlow requires an initial value, SharedFlow does not.
val _stateFlow = MutableStateFlow("Initial State")
val _sharedFlow = MutableSharedFlow<String>()
StateFlow only emits the latest value, while SharedFlow, with the use of the replay operator, can emit previous values as well.
val stateFlow = MutableStateFlow(0)
runBlocking {
stateFlow.value = 1
stateFlow.value = 2
stateFlow.value = 3
launch {
stateFlow.collect {
println(it)
}
}
}
Output:
3
val sharedFlow = MutableSharedFlow<Int>(replay = 3)
runBlocking {
launch {
sharedFlow.emit(0)
sharedFlow.emit(1)
sharedFlow.emit(2)
sharedFlow.emit(3)
sharedFlow.collect() {
println(it)
}
}
}
Output:
1
2
3
StateFlow emits consecutively repeating values only once. SharedFlow emits all values without any condition.
val stateFlow = MutableStateFlow(0)
runBlocking {
launch {
stateFlow.collect {
println(it)
}
}
stateFlow.value = 1
stateFlow.value = 1
stateFlow.value = 2
stateFlow.value = 2
stateFlow.value = 3
}
Output:
0
1
2
3
val sharedFlow = MutableSharedFlow<Int>()
runBlocking {
launch {
sharedFlow.collect {
println(it)
}
}
sharedFlow.emit(0)
sharedFlow.emit(1)
sharedFlow.emit(1)
sharedFlow.emit(2)
sharedFlow.emit(2)
sharedFlow.emit(3)
}
Output:
0
1
1
2
2
3