stateIn and WhileSubscribed(5_000)

Flow and StateFlow Mid-level Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Pick the fix

The Room query re-runs on every rotation. Which change stops that while still stopping in the background?

class NotesViewModel(dao: NoteDao) : ViewModel() {
    val notes: StateFlow<List<Note>> = dao.observeAll()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
}
Options
Hint 1 of 1

How long does a rotation take?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A Use SharingStarted.WhileSubscribed(5_000)

Correct. The 5 s grace period outlasts a rotation, so the upstream keeps running across it.

Why not the others?
  • B Use SharingStarted.Eagerly. That stops the restart but also keeps the query running in the background forever.
  • C Collect the DAO flow directly in the Activity. Then every new Activity starts its own query.
  • D Add .distinctUntilChanged() before stateIn. That removes duplicate values; the restart still happens.

30-second answer

stateIn turns a cold flow, such as a Room query, into a StateFlow the UI can collect. WhileSubscribed(5_000) keeps the upstream running while someone collects and for 5 seconds after the last collector leaves. That covers a rotation, where the UI stops and restarts collecting within a second, without restarting the query, while still stopping it when the app goes to the background.

Practise saying it · 40 s. No recording, just a timer

Explain stateIn with WhileSubscribed(5_000).

Say your answer out loud, then check which of these points you covered:

  • stateIn makes a cold flow a StateFlow
  • Upstream runs while collected plus 5 s
  • Covers rotation
  • Stops in the background
  • Call it once, in a property

Deep dive

Eagerly starts at once and never stops; Lazily starts on the first collector and never stops.

When the upstream restarts after a stop, the StateFlow keeps its last value until the new one arrives.

The initial value is what the UI sees first, so pick a real loading state, not a placeholder that looks like data.

Call stateIn once, in a property; calling it inside a function creates a new StateFlow each time.

The DAO used above Compile-checked · Kotlin 2.4.21
@Entity
data class Note(@PrimaryKey val id: Long, val text: String)

@Dao
interface NoteDao {
    @Query("SELECT * FROM Note ORDER BY id DESC")
    fun observeAll(): Flow<List<Note>>
}

Common mistakes

  • Calling stateIn in a getter or function, creating a new flow per call.
  • Using Eagerly, so a GPS or database stream runs for the ViewModel's whole life.
  • Using WhileSubscribed() with no timeout, so rotation restarts the query.

Senior take & follow-ups

Explain the 5 seconds as rotation slack, and say that ANR or battery issues come from Eagerly on expensive upstreams.

Follow-up questions

Think of your answer first, then open the hint.

  1. What happens if the app is in the background for a minute?

    Show hint

    After 5 s the upstream stops; on return it restarts.

  2. Why not WhileSubscribed(0)?

    Show hint

    Rotation would stop and restart the upstream every time.

Suggest a fix if something here is wrong or out of date.