跪拜 Guibai
← All articles
Android

LiveData Was a Breakthrough, but It's Now the Wrong Abstraction for Most of Your App

By 潜龙勿用之化骨龙 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Teams that continue exposing LiveData from repositories lock themselves into a UI-centric data model that breaks in WorkManager jobs, background services, and tests. Moving to Flow at the data layer and reserving LiveData for thin UI observation removes an Android framework dependency from business logic and gives every consumer the freedom to collect, convert, or sample the same stream.

Summary

LiveData arrived when Android was drowning in callbacks and manual lifecycle checks. Its `observe(owner)` pattern automatically unsubscribed observers and prevented the crashes that came from updating destroyed activities. That single contribution made it indispensable for years. But LiveData models a current value, not a stream of values. It lacks native operators for debouncing, combining, or retrying, forcing developers into `MediatorLiveData` tangles and hand-rolled event wrappers like `SingleLiveEvent` when one-shot UI events are needed. Its threading model, where rapid `postValue` calls may drop intermediate values, confirms it was never built as a message queue. The deeper architectural cost shows up when repositories return `LiveData`. That pulls an Android lifecycle dependency into the data layer, leaving background services and workers without a `LifecycleOwner` to consume the data. Flow, by contrast, is a cold stream that consumers can collect, convert to state, or take a single value from, without the producer dictating the consumption pattern. The practical takeaway is not that LiveData is broken, but that its scope should shrink to the UI layer, where lifecycle awareness is the point, while Flow carries data across the rest of the app.

Takeaways
LiveData is a lifecycle-aware state container, not a general-purpose data stream; it holds a current value and notifies observers.
Flow provides native operators like debounce, filter, combine, retry, and flatMapLatest that LiveData lacks, making complex async pipelines straightforward.
Returning LiveData from a repository forces an Android lifecycle dependency on the data layer, leaving background services and WorkManager jobs without a LifecycleOwner to consume it.
LiveData re-emits its last value on observer re-subscription, which causes one-shot events like navigation or toasts to fire again after screen rotations unless wrapped in SingleLiveEvent or EventWrapper.
Rapid postValue calls can drop intermediate values because LiveData preserves only the latest state, making it unsuitable for event sequences like download progress or message queues.
Flow lets consumers decide how to read the same data source: collectAsState in Compose, collect in a coroutine, or first() in a test, without the producer dictating the pattern.
LiveData still works well for simple UI state that maps directly to lifecycle-bound observers, but complex screens with multiple combined data sources quickly outgrow MediatorLiveData and Transformations.
Conclusions

The shift from LiveData to Flow mirrors a broader industry move from value-holder patterns to stream-oriented architectures, where data is modeled as a continuous flow rather than a snapshot.

LiveData's tight coupling to Android lifecycle components made it a gateway drug for lifecycle safety, but that same coupling became a liability once apps needed to share data with non-UI consumers.

The SingleLiveEvent workaround is a symptom of a category error: LiveData was designed for state, not events, and the community spent years patching that mismatch instead of questioning the abstraction.

Exposing LiveData from a repository is an architectural decision that silently constrains every future consumer; switching to Flow is a one-time cost that pays off the first time a background task needs the same data.

Concepts & terms
LiveData
An Android lifecycle-aware observable data holder that notifies active observers when its value changes and automatically unsubscribes them when the lifecycle owner is destroyed.
Kotlin Flow
A cold asynchronous stream from Kotlin coroutines that supports sequential emission of values and provides operators for transformation, filtering, combining, and error handling.
MediatorLiveData
A LiveData subclass that can observe multiple LiveData sources and react to changes from any of them, commonly used to combine data but requiring manual source management.
SingleLiveEvent
A community pattern that wraps LiveData to ensure a value is consumed only once, preventing re-emission on configuration changes like screen rotations.
LifecycleOwner
An Android interface implemented by components like Activities and Fragments that have a lifecycle; required by LiveData's observe method to manage automatic subscription and cleanup.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗