跪拜 Guibai
← All articles
Android

SharingStarted Is the Real Engine Behind Kotlin's stateIn

By 潜龙勿用之化骨龙 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Misconfiguring `SharingStarted` causes silent bugs: duplicate network requests, stale data flashes, and resource leaks after a screen closes. Picking the right strategy and tuning its two timeout parameters eliminates entire categories of lifecycle-related defects in Android apps.

Summary

Most Android developers treat `stateIn()` as a boilerplate one-liner, but the `SharingStarted` parameter does the heavy lifting. It turns a cold Flow into a hot one and dictates the entire upstream lifecycle: startup trigger, shutdown timing, and cache invalidation. The three built-in strategies—`Eagerly`, `Lazily`, and `WhileSubscribed`—map directly to global data, lazy caches, and UI-bound state.

Two overlooked parameters on `WhileSubscribed` prevent unnecessary work during configuration changes and control whether stale data is replayed. A `stopTimeoutMillis` of 5000 acts as a debounce that keeps the upstream alive across screen rotations, avoiding redundant network calls. Setting `replayExpirationMillis` to zero flushes the cache immediately when subscribers drop, which is essential for real-time data where old values are misleading.

The practical upshot is a set of clear defaults: a 5-second timeout for standard UI state, zero replay expiration for live feeds like stock prices, and `Eagerly` with an application-scoped coroutine for login state or config that must be ready before any UI appears.

Takeaways
`stateIn` converts a cold Flow into a hot one so multiple collectors share a single upstream execution, cutting redundant work like duplicate Room queries.
`SharingStarted.Eagerly` starts the upstream immediately and runs until the scope is destroyed, regardless of collectors.
`SharingStarted.Lazily` starts on the first subscription and then runs until scope destruction, never restarting.
`SharingStarted.WhileSubscribed` starts when a subscriber appears and stops after a configurable timeout once the last subscriber disappears.
The `stopTimeoutMillis` parameter on `WhileSubscribed` keeps the upstream alive briefly during configuration changes, preventing restart storms.
The `replayExpirationMillis` parameter controls how long a cached value survives after all subscribers are gone; the default is `Long.MAX_VALUE` (never expire).
Setting `replayExpirationMillis = 0` immediately clears the cache so users never see stale data, which is critical for real-time feeds like stock prices or countdowns.
A 5-second `stopTimeoutMillis` is the recommended default for most ViewModels; real-time data should add `replayExpirationMillis = 0`; global state should use `Eagerly` with an application-scoped coroutine.
Conclusions

The `WhileSubscribed` timeout is effectively a debounce mechanism for the upstream lifecycle, not just a cleanup delay.

Using `replayExpirationMillis = 0` turns `stateIn` into a strict request-on-subscribe pattern that prevents the flash of stale content users often see when returning to a screen.

The mental model shift from 'stateIn is a caching operator' to 'stateIn is a lifecycle manager with a caching side effect' changes how developers reason about resource cleanup.

Concepts & terms
Cold Flow vs. Hot Flow
A cold Flow executes its producer block anew for each collector. A hot Flow shares a single running producer across all collectors, so multiple subscribers receive the same emissions without re-triggering the upstream work.
SharingStarted.WhileSubscribed
A sharing strategy that starts the upstream Flow when the first subscriber appears and stops it after a configurable timeout once the last subscriber disappears. It is the recommended default for UI-bound state because it aligns with screen lifecycle.
replayExpirationMillis
A parameter on `WhileSubscribed` that determines how long the last emitted value remains cached after all subscribers are gone. A value of 0 discards the cache immediately, forcing a fresh emission on the next subscription.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗