Why is LiveData<Boolean> bad for one-time ViewModel events?

Tests if you know LiveData holds state, not events. A good answer explains that LiveData re-emits on configuration changes, causing repeated events. The solution is to model events as part of the UI state and consume them.
WHAT THIS TESTS: This question probes your understanding of the fundamental difference between state and events in UI programming. It specifically tests your knowledge of LiveData's behavior as a state holder, particularly its tendency to re-emit the last value to new observers (e.g., after a configuration change). An interviewer wants to see if you can identify this common bug and articulate the modern, recommended architectural pattern for handling one-time events reliably.
A GOOD ANSWER COVERS: First, identify the core problem: LiveData is designed to hold and represent the current state. When a configuration change occurs (like a screen rotation), the UI controller (Activity/Fragment) is destroyed and recreated. The new instance subscribes to the ViewModel's LiveData and immediately receives the last emitted value, causing a one-time event like a Toast or navigation to trigger again.
Second, propose the modern, recommended solution: model events as part of your UI state. Instead of a separate LiveData<Event>, the ViewModel exposes a single StateFlow<UiState>. The event (e.g., a message to show) is a nullable property within the UiState data class. When an event needs to be triggered, the ViewModel updates the state with the event data.
Third, explain the consumption mechanism. The UI collects the state flow. When it sees a non-null event, it handles it (e.g., shows a Toast) and immediately calls a function on the ViewModel (e.g., userMessageShown()) to signal that the event has been consumed. The ViewModel then updates the state to set the event property back to null, preventing it from being triggered again.
Fourth, you can briefly mention older patterns like a custom Event wrapper class (often called SingleLiveEvent) as a historical context, but you should frame the state-based approach as superior for its predictability, testability, and alignment with a single source of truth principle.
COMMON WRONG ANSWERS: A major red flag is not understanding why LiveData<Boolean> is a problem. This suggests a lack of experience with Android lifecycles. Another poor answer is suggesting a race-condition-prone fix, like using a Handler to post a delayed _liveData.value = false. The most common mediocre answer is confidently proposing SingleLiveEvent as the definitive solution without acknowledging the modern state-flow-based approach, which indicates outdated knowledge.
LIKELY FOLLOW-UPS: Expect follow-ups like: "How would you implement the state consumption logic in a Fragment using coroutines?" or "What are the specific downsides of the SingleLiveEvent wrapper approach?" (e.g., it can still be problematic with multiple observers). Another likely question is "How does this pattern change or simplify in Jetpack Compose?" (Hint: LaunchedEffect is ideal for this).
ONE CONCRETE EXAMPLE: A ViewModel has val navigateToDetails: LiveData<Boolean>. On success, it sets the value to true. The Fragment observes this and navigates. If the user rotates the screen on the details page and then navigates back, the original Fragment is recreated, observes the true value again, and incorrectly re-navigates. The correct approach is to have a val navigationTarget: NavigationTarget? in a UiState object. The UI navigates when it's non-null, then immediately calls viewModel.onNavigationComplete() to set it back to null.
Read the original → developer.android.com
- #android
- #kotlin
- #jetpack
- #viewmodel
- #architecture
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.