Lifecycle, LiveData & repeatOnLifecycle Interview Questions
ARCHITECTURE › Components
Walk me through what actually happens under the hood when repeatOnLifecycle(STARTED) restarts flow collection as a user backgrounds and foregrounds the screen rapidly. Is there a risk of losing emissions?
What a strong answer covers: repeatOnLifecycle cancels the collecting coroutine on STOP and launches a brand-new one on the next START, so the collector itself is torn down and rebuilt, not paused. Whether emissions are lost depends on the upstream: a StateFlow or a Flow with a replay cache re-delivers its latest value or replay buffer to the new collector, but a plain cold Flow or a SharedFlow with no replay simply drops whatever it emitted while stopped, so the trap is assuming repeatOnLifecycle is loss-free for any arbitrary Flow rather than only ones with conflated or replayed semantics.
In a legacy codebase that still uses LiveData extensively, when would you actually recommend keeping LiveData rather than migrating to StateFlow?
What a strong answer covers: Keep LiveData where the UI is still View-based with many existing observe() call sites and simple map/switchMap bindings, since LiveData's built-in lifecycle awareness avoids having to introduce repeatOnLifecycle boilerplate throughout, and its guaranteed main-thread delivery matches what that code already assumes. Push for StateFlow when the team is moving to Compose or needs Flow operators and coroutine-based testing; the trap is treating the migration as mandatory 'modernization' and rewriting a stable, working View-based screen purely for its own sake.
A screen needs to collect three different flows from a ViewModel simultaneously inside repeatOnLifecycle. Walk me through how you'd structure that, and what mistake would silently break it.
What a strong answer covers: You launch a separate coroutine with launch { } for each flow's collect call inside the repeatOnLifecycle block, so all three run concurrently under the same lifecycle-scoped parent job. The common mistake is writing the collect calls sequentially in one coroutine, since flow.collect suspends indefinitely, so only the first flow is ever actually observed and the other two silently never run, with no exception thrown to indicate the bug.