Lifecycle, LiveData & repeatOnLifecycle Quiz
ARCHITECTURE › Components
An observer is registered with observe(lifecycleOwner, observer). The owner is currently in the CREATED state (e.g. activity stopped). LiveData's value then changes. What happens?
- onChanged runs immediately on a background thread, even then
- It is notified right away just because it is already registered
- It is not notified now, but gets the latest value when STARTED
- LiveData throws an IllegalStateException for inactive owners
Answer: It is not notified now, but gets the latest value when STARTED
LiveData only delivers to active (STARTED/RESUMED) observers. A CREATED-but-not-started owner is inactive, so it gets the most recent value when it next becomes active.
Which statement about repeatOnLifecycle(Lifecycle.State.STARTED) { flow.collect { } } is correct?
- The collect block is cancelled below STARTED, re-launched on return
- It collects just once and caches the result for the whole process lifetime
- It must be passed Lifecycle.State.DESTROYED to ever stop collection
- It runs the collect block on a background thread pool by default
Answer: The collect block is cancelled below STARTED, re-launched on return
repeatOnLifecycle cancels its child coroutine when the state drops below the target and starts a fresh one each time the target state is reached again, repeating until DESTROYED.
Why is launchWhenStarted considered an unsafe/deprecated way to collect a Flow from lifecycleScope?
- It can only be called from onStart, so it is hard to reuse elsewhere
- It always collects on the main thread, which makes it inherently janky
- It never survives configuration changes, so it drops every emission
- It suspends on stop instead of cancelling, so upstream keeps running
Answer: It suspends on stop instead of cancelling, so upstream keeps running
launchWhenX pauses the coroutine but keeps the collection (and its upstream producer) alive in the background, wasting resources. repeatOnLifecycle cancels it outright.
You need to update a LiveData value from a background thread doing network work. Which call is correct?
- Call setValue() since LiveData handles thread dispatch automatically
- Call postValue(), which schedules the update onto the main thread
- Wrap setValue() in runBlocking to make it thread-safe
- Use observeForever() so threading no longer matters
Answer: Call postValue(), which schedules the update onto the main thread
setValue() must run on the main thread and crashes otherwise; postValue() can be called from any thread and posts the value to the main thread.
In Jetpack Compose, what is the idiomatic, lifecycle-aware way to collect a ViewModel's StateFlow?
- viewModel.state.collectAsStateWithLifecycle()
- LaunchedEffect(Unit) { viewModel.state.collect { } } with no lifecycle gating
- viewModel.state.observeAsState() from the LiveData KTX
- remember { viewModel.state.value } read once at composition
Answer: viewModel.state.collectAsStateWithLifecycle()
collectAsStateWithLifecycle (lifecycle-runtime-compose) collects only while at least STARTED and stops in the background, unlike a plain LaunchedEffect collect which keeps running off-screen.
Which scenario is the textbook use case for switchMap rather than map?
- Formatting a User object into a display name string for UI
- Merging a database LiveData and a network LiveData into one
- Mapping a userId LiveData to getUser(userId) LiveData
- Posting a value from a worker thread to the main thread safely
Answer: Mapping a userId LiveData to getUser(userId) LiveData
switchMap is for chaining to a new LiveData per input value and swapping the active source; mapping an id to a repository lookup returning LiveData is the canonical example. map handles plain synchronous transforms.
Which guidance about LiveData vs Flow in a layered architecture is correct for modern Android?
- Return LiveData from repositories so its transformations run off the main thread
- Use LiveData everywhere, since Flow is never lifecycle-aware in Android
- Never use LiveData; AndroidX removed it and Flow is the only option
- Keep Flow in the data layer, expose StateFlow or asLiveData() to UI
Answer: Keep Flow in the data layer, expose StateFlow or asLiveData() to UI
Flow belongs in the data layer (threading, operators, composition); the ViewModel exposes it as lifecycle-friendly StateFlow or via asLiveData(). LiveData transforms run on the main thread, so repositories should not return LiveData.
You call liveData.observeForever(observer) from a long-lived singleton. What is the key risk compared with observe(owner, observer)?
- It throws at runtime since observeForever still needs a LifecycleOwner
- The observer stays permanently active, never auto-removed, so it leaks
- It only delivers values while some Activity is in the RESUMED state
- It silently drops every update until a LifecycleOwner is attached later
Answer: The observer stays permanently active, never auto-removed, so it leaks
observeForever has no lifecycle to bound it, so the observer stays active forever and LiveData never removes it automatically; forgetting removeObserver leaks the observer and everything it references.
When observing LiveData inside a Fragment that inflates a view, which LifecycleOwner should you pass to observe()?
- viewLifecycleOwner, since the view can be recreated separately
- this (the Fragment), because its view always lives as long
- requireActivity(), so the observer stays alive across recreates
- ProcessLifecycleOwner.get(), to bind it to the whole app lifecycle
Answer: viewLifecycleOwner, since the view can be recreated separately
A Fragment's view can be destroyed and recreated (e.g. while on the back stack) while the Fragment instance survives, so observing with viewLifecycleOwner scopes the observer to the current view and avoids leaks and duplicate observers.
You write lifecycleScope.launch { repeatOnLifecycle(STARTED) { collectA() }; collectB() }. What happens to collectB()?
- collectB runs alongside collectA as soon as the coroutine starts
- collectB is started again each time the lifecycle reaches STARTED
- collectB waits until DESTROYED, as repeatOnLifecycle suspends there
- collectB runs once right away, then repeatOnLifecycle handles collectA
Answer: collectB waits until DESTROYED, as repeatOnLifecycle suspends there
repeatOnLifecycle is a suspend function that only returns once the lifecycle is DESTROYED, so code placed after it in the same coroutine is effectively unreachable while the screen is alive; each collector must live inside the repeatOnLifecycle block.
You need a single LiveData that emits whenever EITHER a cached LiveData or a network LiveData changes. Which API fits best?
- map { } applied to whichever source you consider primary
- switchMap { } keyed on a boolean that picks one source
- Two independent observers each writing into a plain MutableLiveData with no coordination
- MediatorLiveData, calling addSource() for each input
Answer: MediatorLiveData, calling addSource() for each input
MediatorLiveData.addSource() observes multiple inputs and emits a combined result when any of them changes, whereas map and switchMap each transform only a single upstream source.
A background loop calls postValue(1), postValue(2), postValue(3) in rapid succession before the main thread gets a chance to run. What is guaranteed about what observers see?
- Observers receive 1, then 2, then 3 in that exact order on the UI thread
- Observers may see only 3, since pending postValue updates coalesce
- Observers receive nothing because postValue cannot be called from a loop
- Observers receive 1 only, since the first scheduled value always wins
Answer: Observers may see only 3, since pending postValue updates coalesce
postValue schedules a task on the main thread, and if several posts are queued before that task runs, only the most recent value is dispatched, so intermediate values can be dropped.
Which statement correctly distinguishes StateFlow from a plain SharedFlow when exposing UI state from a ViewModel?
- StateFlow holds one current value, requires an initial, and conflates updates
- StateFlow has no current value and never replays anything to new collectors
- StateFlow buffers every emission and delivers all values with no conflation
- StateFlow is hot only while an Activity is RESUMED and becomes cold otherwise
Answer: StateFlow holds one current value, requires an initial, and conflates updates
StateFlow is a conflated state holder: it requires an initial value, always exposes a current value, and replays only the latest value to new collectors, which is exactly why it suits UI state; SharedFlow is more configurable and need not hold a value.
Why is implementing DefaultLifecycleObserver preferred over the older @OnLifecycleEvent-annotated LifecycleObserver?
- @OnLifecycleEvent dispatches on a background thread, while DefaultLifecycleObserver is main-thread only.
- Only DefaultLifecycleObserver can observe ProcessLifecycleOwner across the app process.
- The @OnLifecycleEvent API is deprecated; DefaultLifecycleObserver is type-safe and reflection-free.
- DefaultLifecycleObserver is the only observer you can register with lifecycle.addObserver().
Answer: The @OnLifecycleEvent API is deprecated; DefaultLifecycleObserver is type-safe and reflection-free.
@OnLifecycleEvent was deprecated in favor of DefaultLifecycleObserver, whose overridable onCreate/onStart/onStop callbacks are type-safe and avoid the reflection and annotation processing the old approach relied on.