With SharingStarted.WhileSubscribed, what happens to the upstream coroutine across subscriber gaps, and what are the resource and correctness implications versus Eagerly?
answer
- subscriptionCount 0 -> STOP cancels upstream
- STOP then START re-collects cold upstream from scratch
- Eagerly never restarts but never releases
- 5000ms window survives rotation
- Watch idempotency of upstream side effects on restart
basics
~10 sWhileSubscribed cancels the upstream when nobody is listening (after a timeout) and restarts it when a collector returns, so expensive work pauses and resumes. Eagerly keeps it running forever, wasting resources but never restarting.
solid answer
~40 sWhileSubscribed ties the upstream's lifetime to subscriptionCount: when it drops to zero, after stopTimeoutMillis the sharing coroutine cancels the upstream collection (e.g. a Room query, a Firebase listener, a websocket); when a subscriber returns, the upstream is collected fresh. This frees resources while the screen is gone but means the upstream restarts — so a cold upstream's setup cost (DB query, network handshake) is paid again, and any in-flight state is reset toward the replay cache / initialValue. Eagerly never stops the upstream until the hosting scope is cancelled: no restart cost, latest value always warm, but the upstream runs even when nothing observes it, which leaks resources for expensive sources. The standard compromise is WhileSubscribed(5000) so brief unsubscribe gaps (config changes) don't trigger a restart while still releasing resources on real teardown.
go deeper
Understands WhileSubscribed stops work when nobody listens and Eagerly keeps running.
Explains the start/stop on subscriptionCount and why 5000ms is used for rotation.
Reasons about upstream restart cost, resource teardown vs leak, replay-cache reset, and idempotency of side effects.
Weighs cost models across the app (battery, connections), defines policies per source type, and designs idempotent/resumable upstreams to make restarts safe.
## The sharing coroutine and subscriptionCount `shareIn`/`stateIn` launch one coroutine in the provided `scope`. `SharingStarted.WhileSubscribed` watches the `SharedFlow.subscriptionCount` and emits: - `START` when count goes 0 -> 1, - `STOP` (after `stopTimeoutMillis`) when it returns to 0, - optionally `STOP_AND_RESET_REPLAY_CACHE` after `replayExpirationMillis`. A `STOP` **cancels the upstream collection job**. The upstream is a cold flow, so the next `START` **re-collects it from scratch**. ## Implications of restart - An upstream like `roomDao.observeUser()` or `callbackFlow { firestore.addSnapshotListener(...) }` is torn down on STOP and re-established on START. The teardown is correct (no leaked listener); the restart re-runs the query / re-registers the listener. - Any per-subscription expensive handshake (websocket connect, auth) is paid again on each restart. - For a `StateFlow`, on `STOP_AND_RESET_REPLAY_CACHE` the value resets toward `initialValue`; otherwise the last value is retained and instantly available to the next subscriber. ## Eagerly contrast ```kotlin .stateIn(scope, SharingStarted.Eagerly, initial) ``` - Upstream runs from creation until `scope` cancellation, **independent of subscribers**. - Pros: no restart cost, value always current. - Cons: the source keeps consuming resources (open socket, polling, battery) even when no UI observes it — a real leak for expensive sources. ## Choosing the timeout `WhileSubscribed(5000)` is the canonical Android value: a configuration change unsubscribes then re-subscribes within milliseconds, so the 5s window prevents an unnecessary upstream restart while still releasing resources after a genuine navigation away. ```kotlin val state = repo.streamExpensive() .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), Initial) ``` ## Correctness caution If the upstream has side effects on start (incrementing a counter, sending analytics), repeated WhileSubscribed restarts can fire them multiple times. Make upstream start idempotent or pick Eagerly/Lazily when restarts are unacceptable.
- Your websocket reconnects every time the user rotates the screen. Why, and how do you fix it?Likely WhileSubscribed with a too-short (or zero) timeout, so the brief unsubscribe on rotation triggers STOP and a reconnect. Use WhileSubscribed(5000) or higher, or Lazily/Eagerly if reconnection is too costly.
- Does WhileSubscribed help with a Room Flow specifically?Yes — it cancels the database observation when no UI is collecting, freeing the cursor/observer, and resumes it on return without you managing lifecycles manually.
WhileSubscribed is a motion-sensor light: off when the room is empty, on when someone enters — but it takes a moment to warm up each time.
saying these in an interview costs you the question
- Claiming WhileSubscribed never restarts the upstream
- Saying Eagerly releases resources when idle
- Ignoring that cold upstream re-runs its producer on each restart
- Not considering non-idempotent upstream side effects
- Assuming the StateFlow value is always reset on STOP (only with replay expiration)