Compare SharingStarted.Eagerly, Lazily, and WhileSubscribed. How does each control when the shared upstream starts and stops?
answer
- Eagerly: start now, stop on scope cancel
- Lazily: start on first collector, then stay
- WhileSubscribed: start first sub, stop after last + timeout
- WhileSubscribed(5000) survives rotation
- Commands: START / STOP / STOP_AND_RESET_REPLAY_CACHE
basics
~20 sEagerly starts the upstream right away and never stops. Lazily starts on the first collector and never stops. WhileSubscribed starts on the first collector and stops when the last one leaves, optionally after a timeout.
solid answer
~40 sSharingStarted is the policy passed to shareIn/stateIn that commands the sharing coroutine when to start and stop the upstream subscription. Eagerly: upstream starts immediately when shareIn/stateIn is called and runs until the scope is cancelled — collectors may miss early emissions if replay is small. Lazily: upstream starts when the first subscriber appears, then stays active until scope cancellation, even after all subscribers leave. WhileSubscribed(stopTimeoutMillis, replayExpirationMillis): upstream starts on first subscriber and stops stopTimeoutMillis after the last unsubscribes; this is the standard Android choice (e.g. WhileSubscribed(5000)) so a config change/rotation doesn't restart the upstream. replayExpirationMillis controls when the cached replay is reset to the initial value after stopping. You can also implement custom SharingStarted by returning SharingCommand values.
code
kotlin · 7 linesval uiState = repository.observe()
.map { it.toUiState() }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = UiState.Loading,
)go deeper
Can name the three policies and give a one-line meaning for each.
Accurately describes start/stop timing for all three and picks WhileSubscribed(5000) for UI with a correct reason.
Explains the underlying SharingCommand mechanism, subscriptionCount, and replayExpirationMillis trade-offs.
Reasons about resource lifetime/cost models, custom SharingStarted policies, and how the choice interacts with scope cancellation across the app architecture.
## What SharingStarted is `SharingStarted` is a strategy object passed to `shareIn`/`stateIn`. Internally the sharing coroutine observes the **number of active subscribers** (`subscriptionCount`) and the policy emits `SharingCommand` values: `START`, `STOP`, or `STOP_AND_RESET_REPLAY_CACHE`. Those commands turn the upstream collection on and off. ## Eagerly ```kotlin upstream.stateIn(scope, SharingStarted.Eagerly, initial) ``` - Upstream **starts immediately** when the operator is called, regardless of subscribers. - **Never stops** until the `scope` is cancelled. - Risk: values emitted before any collector arrives are lost unless they fit in the `replay` cache (StateFlow keeps the latest). ## Lazily - Upstream **starts on the first subscriber**. - Once started, **stays active** until the scope is cancelled, even if all subscribers disappear. - Good when you want to avoid work until someone cares, but keep it warm afterward. ## WhileSubscribed ```kotlin SharingStarted.WhileSubscribed( stopTimeoutMillis = 5_000, replayExpirationMillis = Long.MAX_VALUE ) ``` - Upstream **starts on the first subscriber** and **stops `stopTimeoutMillis` after the last** subscriber leaves. - The timeout avoids restarting the upstream during brief subscriber gaps — the classic case is **Android configuration changes** (screen rotation) where the collector momentarily unsubscribes. `WhileSubscribed(5000)` keeps it alive across that gap. - `replayExpirationMillis`: after the upstream stops, how long before the replay cache is dropped (and a StateFlow resets toward its initial value via `STOP_AND_RESET_REPLAY_CACHE`). Default `Long.MAX_VALUE` keeps the last value forever. ## Choosing - Always-needed, cheap, want latest at app start: `Eagerly`. - Expensive, start-on-demand, keep warm: `Lazily`. - UI-bound, want to stop work when screen is gone but survive rotation: `WhileSubscribed(5000)`. ## Custom You can implement `SharingStarted` yourself by returning a `Flow<SharingCommand>` from `command(subscriptionCount)`.
- Why is WhileSubscribed(5000) the Android default for UI state?On a configuration change the collector unsubscribes and re-subscribes within milliseconds; the 5s timeout keeps the upstream (e.g. a DB query) alive so it isn't torn down and restarted needlessly.
- What does replayExpirationMillis = 0 do with WhileSubscribed?It emits STOP_AND_RESET_REPLAY_CACHE immediately when the last subscriber leaves, so the replay cache is dropped and a StateFlow resets to its initialValue.
saying these in an interview costs you the question
- Saying Lazily stops the upstream when subscribers leave
- Claiming Eagerly waits for a subscriber
- Not knowing WhileSubscribed has a stop timeout
- Thinking the timeout's purpose is throttling rather than surviving brief unsubscribe gaps
- Believing SharingStarted controls replay count (it doesn't)