Choose based on what a late subscriber needs. A screen opening now needs the current lesson state, not a replay of every prior loading transition. StateFlow supplies a current value and requires an initial one. Equal values are suppressed, and a slow collector can skip intermediate updates.
SharedFlow exposes a configurable broadcast stream. Its replay count determines what a new subscriber receives; buffering and overflow policy affect active slow subscribers. It does not automatically suppress equal values.
For a local counter, keep mutation private and publish read-only state:
private val _completed = MutableStateFlow(0)
val completed: StateFlow<Int> = _completed.asStateFlow()
fun completeLesson() {
_completed.update { count -> count + 1 }
}
The update is atomic. Its lambda may be evaluated more than once under contention, so keep side effects such as charging a payment outside it.
“Use SharedFlow for every one-off event” misses delivery requirements. With zero replay, an emission while no subscriber exists is lost. Extra buffer capacity does not create an offline queue for absent subscribers. Replay can instead cause an action to be seen again after recreation.
For a purchase confirmation that must remain visible, model an outcome as state and define acknowledgement or persistence. A transient animation signal may tolerate loss. Choose deliberately.
Does StateFlow deliver every increment? Its latest value is available, but a slow collector may skip intermediate values. Use an appropriate event log or queue when every transition matters.
Mark this when you can explain the answer in your own words.
Help fellow developers prepare for interviews
Sharing helps the Android community grow 💚