LiveData Internals Explained: Why postValue Drops Data & How Lifecycle Awareness Works
A deep dive into ObserverWrapper, mVersion tracking, and why postValue() might be dropping your data.
⚡ TL;DR: The 60-Second Summary
- Lifecycle Awareness: LiveData wraps observers in
LifecycleBoundObserverto monitorisAtLeast(STARTED). - postValue vs setValue:
postValuecoalesces updates into a single batch; it drops intermediate values to avoid flooding the Main Thread. - Sticky Behavior: Managed via an internal
mVersioncounter compared against the observer’smLastVersion. - Memory Safety: Automatically removes observers in
DESTROYEDstate (except forobserveForever). - Active Hooks: Uses
onActive()andonInactive()to manage resource-heavy data sources like Room or GPS.
Most Android developers use LiveData daily as the default state holder in MVVM architecture in Android. As a core part of Android Architecture Components, it is designed specifically to help build resilient, lifecycle-aware apps.
However, very few can explain the source code logic that prevents crashes or why postValue behaves differently than setValue. If you're preparing for a Senior Android interview, let's open LiveData.java and look at the gears turning inside.
๐ 1. The Entry Point: What Happens Inside observe()?
When you call liveData.observe(lifecycleOwner, observer), LiveData doesn't just add your callback to a list. It creates a relationship by wrapping it.
LifecycleBoundObserver: The standard wrapper. It listens to theLifecycleOwnerand only dispatches data if the state is at leastSTARTED(i.e.,STARTEDorRESUMED).- The Guard Clause: If you call
observewhile the Lifecycle is alreadyDESTROYED, LiveData ignores the call. - Unique Constraint: LiveData prevents the same
Observerinstance from being attached to multiple differentLifecycleOwners. Doing so triggers anIllegalArgumentException.
๐ 2. setValue() vs. postValue(): The Coalescing Engine
This is a classic interview deep-dive. While setValue() is a direct synchronous update on the Main Thread, postValue() is designed for efficiency over frequency.
The “Single Runnable” Rule
When you call postValue(), LiveData doesn't queue a new task for every single call. It uses a coalescing strategy:
- It synchronizes on an internal
dataLock. - It checks a boolean: Is there already a pending update?
- If yes, it updates the data and returns.
- If no, it posts exactly one
Runnableto the Main Thread.
Why? This prevents the UI thread from being overwhelmed. If you post 100 updates in a background loop before the Main Thread executes, the UI will only receive the latest (100th) value.
⚠️ Senior Nuance: Mixing
setValue()andpostValue()can lead to non-deterministic ordering. SincepostValue()is asynchronous, a latersetValue()call on the Main Thread might be delivered before an earlierpostValue()from a background thread.
// Internal logic of postValue (Simplified)
protected fun postValue(value: T) {
val postTask: Boolean
synchronized(dataLock) {
postTask = mPendingData == NOT_SET // Is the queue empty?
mPendingData = value
}
if (!postTask) return // Task already in flight, just updated the data.
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable)
}๐ 3. Why is LiveData “Sticky”? (The mVersion Secret)
Have you noticed that a Fragment gets the latest data immediately upon rotating the screen? That is “Sticky” behavior, managed by versioning.
mVersion: An internal integer that increments on every update.mLastVersion: An integer stored inside eachObserverWrapper.
When the lifecycle moves from an inactive state (below STARTED) to STARTED, LiveData triggers a dispatchingValue(). It compares the versions:
Logic:
if (wrapper.mLastVersion < mVersion)→ Dispatch Data.
๐ 4. Internal Thread Safety: SafeIterableMap
Inside the source code, observers are stored in a SafeIterableMap.
- The Architecture: It’s a hybrid between a Linked List and a HashMap.
- The Solution: Standard collections throw a
ConcurrentModificationExceptionif you modify them while iterating.SafeIterableMapallows observers to be added or removed safely during a dispatch loop.
๐ 5. Active State Hooks: onActive() & onInactive()
LiveData tracks the number of “Active” observers (those in STARTED or RESUMED).
onActive(): Called when the active observer count goes from 0 to 1.onInactive(): Called when the count drops to 0.
These hooks are powerful for system design. Room, for example, uses onActive() to start listening to database changes and onInactive() to close the cursor, saving memory and battery.
๐ก Real-World Interview Answer: “How does LiveData work?”
“LiveData is a lifecycle-aware state holder from the Android Architecture Components. It wraps observers in a
LifecycleBoundObserverthat tracks theLifecycle.Stateto ensure data is only dispatched when the UI is at leastSTARTED. It manages thread-safety viapostValue, which uses a coalescing mechanism to batch updates and avoid flooding the Main Thread. It also automates memory management by unsubscribing observers when the lifecycle hitsDESTROYED."
๐♂️ Frequently Asked Questions (FAQs)
Does postValue guarantee every update is received?
No. Because of coalescing, intermediate values are dropped if they happen rapidly. Use Kotlin Flow (SharedFlow) if every emission matters.
Can I use LiveData in a Repository?
It works, but it’s discouraged. It ties your data layer to the Android Main Thread. Use Flow for a platform-independent, testable architecture.
Why use viewLifecycleOwner in Fragments?
Fragment instances often outlive their Views. Using this can lead to duplicate observers when a user navigates back to a Fragment.
๐ Final Thoughts
Understanding the internals of the tools we use makes us better architects. LiveData is excellent for UI state, but its batching nature and lack of operators mean it shouldn’t be your only tool for reactive streams.
Let’s Discuss!
- Have you ever had a bug caused by
postValuedropping intermediate states? - Are you still using LiveData for new projects, or have you fully migrated to
StateFlow? - What other Jetpack component internals should we dive into next?
๐ Master Your Next Technical Interview
Since Java is the foundation of Android development, mastering DSA is essential. I highly recommend “Mastering Data Structures & Algorithms in Java”. It’s a focused roadmap covering 100+ coding challenges to help you ace your technical rounds.
- E-book (Best Value! ๐): $1.99 on Google Play
- Kindle Edition: $3.49 on Amazon
- Also available in Paperback & Hardcover.

Comments
Post a Comment