StateFlow
and
SharedFlow
Module: “Basics of Kotlin Flow”
ViewModel
Activity
observes
LiveData<UiState>
Module: “StateFlow and SharedFlow”
ViewModel
Activity
collects
Flow<UiState>
Antipattern
using LiveData in other Layers
than the UI Layer
Advantages: Exposing Flows instead of LiveData in ViewModels
A Single type of observable data holder throughout your architecture
No knowledge about LiveData necessary
More ow operators
ViewModels are decoupled from Android Dependencies
Simpli ed testing
fl
fi
Disadvantages: Exposing Flows instead of LiveData in ViewModels
more “boilerplate” code in the view
Summary: Exposing Flows instead of LiveData in ViewModels
existing Code: 🤔
new Code: 🙂
Approach 1: Exposing regular ow
Activity ViewModel
lifecycleScope.launch {
viewModel.currentStockPriceAsFlow.collect { val currentStockPriceAsFlow: Flow<UiState>
render(uiState)
}
}
Problems ❌
• Flow Producer continues to run when the app is in background
• Activity receives emissions and renders UI when it is in the background
• Multiple collectors create multiple ows
• Con guration Change re-starts the ow
fi
fl
fl
fl
Approach 2: “lifecycle-aware” collecting coroutine
Activity ViewModel
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
val currentStockPriceAsFlow: Flow<UiState>
viewModel.currentStockPriceAsFlow.collect {
render(uiState)
}
}
}
Problems ❌
• Flow Producer continues to run when the app is in background
• Activity receives emissions and renders UI when it is in the background
• Multiple collectors create multiple ows
• Con guration Change re-starts the ow
fi
fl
fl
Approach 2: “lifecycle-aware” collecting coroutine
Activity ViewModel
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
val currentStockPriceAsFlow: Flow<UiState>
viewModel.currentStockPriceAsFlow.collect {
render(uiState)
}
}
}
Problems ❌
• Flow Producer continues to run when the app is in background
• Activity receives emissions and renders UI when it is in the background
• Multiple collectors create multiple ows
• Con guration Change re-starts the ow
fi
fl
fl
Approach 2: “lifecycle-aware” collecting coroutine
Activity ViewModel
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
val currentStockPriceAsFlow: Flow<UiState>
viewModel.currentStockPriceAsFlow.collect {
render(uiState)
}
}
}
Problems ❌
• Flow Producer continues to run when the app is in background ✅
• Activity receives emissions and renders UI when it is in the background
• Multiple collectors create multiple ows
• Con guration Change re-starts the ow
fi
fl
fl
Approach 2: “lifecycle-aware” collecting coroutine
Activity ViewModel
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
val currentStockPriceAsFlow: Flow<UiState>
viewModel.currentStockPriceAsFlow.collect {
render(uiState)
}
}
}
Problems ❌
• Flow Producer continues to run when the app is in background ✅
• Activity receives emissions and renders UI when it is in the background ✅
• Multiple collectors create multiple ows
• Con guration Change re-starts the ow
fi
fl
fl
Approach 3: Exposing a “hot” ow in the ViewModel
Activity ViewModel
lifecycleScope.launch { val currentStockPriceAsFlow: SharedFlow<UiState>
repeatOnLifecycle(Lifecycle.State.STARTED) { // = coldflow
viewModel.currentStockPriceAsFlow.collect { .shareIn(
render(uiState) scope = viewModelScope,
} started = SharingStarted.WhileSubscribed()
} )
}
Problems ❌
• Flow Producer continues to run when the app is in background ✅
• Activity receives emissions and renders UI when it is in the background ✅
• Multiple collectors create multiple ows
• Con guration Change re-starts the ow
fi
fl
fl
fl
Approach 3: Exposing a “hot” ow in the ViewModel
Activity ViewModel
lifecycleScope.launch { val currentStockPriceAsFlow: SharedFlow<UiState>
repeatOnLifecycle(Lifecycle.State.STARTED) { // = coldflow
viewModel.currentStockPriceAsFlow.collect { .shareIn(
render(uiState) scope = viewModelScope,
} started = SharingStarted.WhileSubscribed()
} )
}
Problems ❌
• Flow Producer continues to run when the app is in background ✅
• Activity receives emissions and renders UI when it is in the background ✅
• Multiple collectors create multiple ows ✅
• Con guration Change re-starts the ow
fi
fl
fl
fl
Cold Flows ❄ Hot Flows 🔥
• become active on collection • are active regardless of
whether there are collectors
• become inactive on
cancellation of the collecting • stay active even when there is
coroutine no more collector
• emit individual emissions to • emissions are shared
every collector between all collectors
con guration change without timeout
latestStockFlow
old Activity instance
onStop() stop collection stop collection
SharedFlow
t
new Activity instance start collection start collection
onCreate()
latestStockFlow
fi
con guration change with timeout of 5000ms
old Activity instance
onStop() stop collection
latestStockFlow
wait 5000 ms for new
SharedFlow
collectors
before stopping the
t upstream collection
new Activity instance start collection
onCreate()
fi
Problem: blank screen after orientation change
emission n
old Activity instance
onStop() stop collection
SharedFlow
t
new Activity instance start collection
onCreate()
blank
screen
emission n+1
Problem: blank screen after orientation change
emission n
old Activity instance
onStop() stop collection
SharedFlow
t
new Activity instance start collection
onCreate() emission n
emission n+1
Approach 3: Exposing a “hot” ow in the ViewModel
Activity ViewModel
val currentStockPriceAsFlow: SharedFlow<UiState>
lifecycleScope.launch {
// = coldflow
repeatOnLifecycle(Lifecycle.State.STARTED) {
.shareIn(
viewModel.currentStockPriceAsFlow.collect {
scope = viewModelScope,
render(uiState)
started = SharingStarted.WhileSubscribed(5000),
}
replay = 1
} )
}
Problems ❌
• Flow Producer continues to run when the app is in background ✅
• Activity receives emissions and renders UI when it is in the background ✅
• Multiple collectors create multiple ows ✅
• Con guration Change re-starts the ow
fi
fl
fl
fl
Approach 3: Exposing a “hot” ow in the ViewModel
Activity ViewModel
val currentStockPriceAsFlow: SharedFlow<UiState>
lifecycleScope.launch {
// = coldflow
repeatOnLifecycle(Lifecycle.State.STARTED) {
.shareIn(
viewModel.currentStockPriceAsFlow.collect {
scope = viewModelScope,
render(uiState)
started = SharingStarted.WhileSubscribed(5000),
}
replay = 1
} )
}
Problems ❌
• Flow Producer continues to run when the app is in background ✅
• Activity receives emissions and renders UI when it is in the background ✅
• Multiple collectors create multiple ows ✅
• Con guration Change re-starts the ow ✅
fi
fl
fl
fl
SharedFlow
VS 🤺
StateFlow
SharedFlow StateFlow
Initial Value No Yes
Replay Cache customizable xed size of 1
Emission of
yes no
subsequent equal values
fi
SharedFlow StateFlow
Initial Value No Yes
Replay Cache customisable xed size of 1
Emission of yes no
subsequent equal values
Rule of Thumb of Usage:
Whenever you want to use a hot ow, use a StateFlow by default.
StateFlows are more e cient when used for state
StateFlows provide convenient option to read and write its value in a non-suspending fashion by
synchronously accessing the .value property
Only if you have special requirements, switch to a SharedFlow.
fi
ffi
fl
Attention!
In Module 17 about “Concurrent Flows”, you will learn about an additional
di erence between SharedFlows and StateFlows.
With StateFlows, you can potentially “lose” emissions if you have a slow
collector.
You can nd more information about this behaviour in the lecture “Bu ers
in SharedFlow and StateFlow”
ff
fi
ff