skip to content

Explain the replay parameter of shareIn and the initialValue of stateIn, and how a new collector experiences each.

level: middleimportance: should knowfreq 50%

answer

  1. replay = size of the re-emit cache
  2. replay = 0 -> only future values
  3. stateIn = replay 1 + conflate + distinct
  4. initialValue seeds .value before first emission
  5. Suspending stateIn waits for first value (no initial)

basics

~10 s

shareIn's replay says how many recent values a brand-new collector instantly receives. stateIn always replays exactly the one latest value, and its initialValue is what collectors see before the upstream has emitted anything.

solid answer

~40 s

shareIn(scope, started, replay) keeps a cache of the last `replay` emitted values; any new collector immediately receives those cached values, then live ones. replay = 0 (default) means new collectors only get future emissions. stateIn is effectively shareIn with replay = 1 plus conflation and distinct-until-changed: it always holds exactly one current value reachable via .value. Its initialValue seeds that current value so .value and new collectors have something before the upstream emits. Because StateFlow conflates, a slow collector may skip intermediate values and only see the latest. SharedFlow with a buffer can also conflate or suspend depending on onBufferOverflow, but plain shareIn uses replay for the cache, not back-pressure tuning.

go deeper

for a junior

Knows replay re-emits recent values and initialValue is the starting value.

for a middle

Explains replay = 0 vs n, and that stateIn behaves like replay 1 with conflation and distinct-until-changed.

for a senior

Discusses conflation/de-dup consequences for event delivery and when to choose SharedFlow over StateFlow.

for a principal

Reasons about correctness of state vs event semantics across the system and chooses replay/initialValue to match consumer contracts.

## shareIn replay ```kotlin fun <T> Flow<T>.shareIn(scope, started, replay: Int = 0): SharedFlow<T> ``` `replay` is the size of the **replay cache** — the count of most-recent values stored and re-emitted to every *new* collector before it starts receiving live emissions. - `replay = 0`: new collectors see only values emitted *after* they subscribe. - `replay = 1`: a new collector immediately gets the single latest value (a 'last known value' cache). - `replay = n`: the last `n` values are replayed in order. ## stateIn initialValue ```kotlin fun <T> Flow<T>.stateIn(scope, started, initialValue: T): StateFlow<T> ``` A `StateFlow` always has a current value. Before the upstream emits, that value is `initialValue`. It behaves like `replay = 1` **plus**: - **Conflation**: only the latest value is retained; fast upstream emissions overwrite older un-collected ones. - **Distinct-until-changed**: a new value equal (`==`) to the current one is *not* re-emitted. ## What a new collector sees - `shareIn(replay = 0)`: nothing until the next live emission. - `shareIn(replay = 2)`: the last two cached values, then live. - `stateIn(initial = X)`: immediately the current `.value` (which is `X` until the upstream produces something). ```kotlin val s = flowOf(1, 2, 3).stateIn(scope, SharingStarted.Eagerly, initialValue = 0) // new collector immediately gets the latest value (3 after upstream finished), never 0 if already emitted println(s.value) ``` ## Suspending stateIn There is also a **suspending** `stateIn(scope)` overload that has no initialValue: it suspends until the upstream emits the first value and uses that as the initial state. Use it when no sensible seed exists but you can await the first item. ## Conflation caveat Because StateFlow conflates and de-dups, do not use it for an event stream where every emission and duplicates must be delivered — use SharedFlow (via shareIn or MutableSharedFlow) instead.

  • Can a StateFlow drop values?
    Yes — it conflates, so a slow collector may miss intermediate values and equal consecutive values are de-duplicated; only the latest distinct value is guaranteed.
  • When would you prefer the suspending stateIn overload?
    When there is no meaningful initial seed but you can afford to suspend until the upstream produces its first value, which then becomes the StateFlow's initial state.

saying these in an interview costs you the question

  • Saying replay controls how long values are kept by time (it's a count)
  • Claiming StateFlow delivers every intermediate value
  • Thinking initialValue is emitted even after the upstream has produced values
  • Confusing replay cache with a buffer for back-pressure
  • Not knowing a suspending stateIn overload exists

context