LiveData 源码解析 📅 发布时间:2026/9/13 23:46:48 👁 浏览次数: LiveData 是 Android 中基于观察者模式、并与 Lifecycle 组件绑定的数据容器能自动管理订阅关系。它的核心价值在于生命周期感知和自动资源管理界面不可见时自动停止分发恢复时立即同步最新数据。三大关键机制包括版本号机制防止数据倒灌和重复消费、setValue 同步与 postValue 异步很多陷阱的根源、以及粘性事件注册后立即收到最新数据。它非常适合在 Activity/Fragment 中承载 UI 状态但处理一次性事件时需借助 SingleLiveEvent、Event 包装类或 SharedFlow 等方案。一、核心设计观察者模式 生命周期感知LiveData 本质上是一个数据容器你可以往里面放数据也可以注册观察者监听变化。但它和普通观察者模式最大的区别是与 Lifecycle 组件绑定能自动管理订阅关系。关键角色类/接口职责LiveDataT持有数据mData、观察者列表mObservers、版本号mVersionObserverT用户实现的接口只有onChanged(T data)方法LifecycleOwner通常是 Activity/Fragment提供生命周期状态LifecycleBoundObserver最重要的内部类连接三者的胶水LifecycleBoundObserver既包装了你的 Observer 和 LifecycleOwner又实现了LifecycleEventObserver从而能监听传入的LifecycleOwner通常是 Activity/Fragment生命周期变化。二、三大核心场景场景 1订阅的建立 ——observe()当你调用observe()时发生了这些事安全检查如果组件已 DESTROYED直接返回防止内存泄漏创建对象把 Observer 和 LifecycleOwner 打包成LifecycleBoundObserver双向绑定LiveData 的mObservers持有这个 wrapperwrapper 通过addObserver把自己注册为 LifecycleOwner 的生命周期观察者结果Activity 的任何生命周期变化都会通知到 wrapper这是生命周期感知的基础。场景 2数据分发 ——setValue()/postValue()setValue(T value)—— 主线程更新MainThread protected void setValue(T value) { assertMainThread(setValue); // 必须在主线程 mVersion; // 版本号升级关键 mData value; // 更新数据 dispatchingValue(null); // 开始分发 }postValue(T value)—— 任意线程更新它只是把setValue打包成 Runnablepost 到主线程消息队列然后立即返回。considerNotify()—— 决定是否通知三重过滤private void considerNotify(ObserverWrapper observer) { // 检查1观察者是否活跃不活跃不通知避免更新已停止的UI if (!observer.mActive) return; // 检查2生命周期是否正常 if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } // 检查3版本号检查防止重复通知 if (observer.mLastVersion mVersion) return; // 通过所有检查发送通知 observer.mLastVersion mVersion; observer.mObserver.onChanged((T) mData); }场景 3生命周期感知LifecycleBoundObserver通过监听生命周期事件来切换活跃状态Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { Lifecycle.State currentState mOwner.getLifecycle().getCurrentState(); if (currentState DESTROYED) { removeObserver(mObserver); // 发现 Activity destroy 了自动移除订阅防内存泄漏 return; } boolean newActiveState currentState.isAtLeast(STARTED); activeStateChanged(newActiveState); }关键点当从非活跃变为活跃时会立即触发一次数据分发。这就是为什么界面一恢复就能显示最新数据。也称粘性事件三、精妙设计1. 版本号机制mVersion mLastVersion解决的问题防止数据倒灌和重复消费。场景Activity 在后台时LiveData 变化了 5 次A→B→C→D→E。恢复前台时不应该收到 5 次回调而只应收到一次最新的 E。原理LiveData 有全局版本mVersion每个观察者有mLastVersion。只有mLastVersion mVersion时才通知。恢复时mLastVersion远小于最新mVersion只触发一次回调后更新版本号后续检查就不通过了。2. onActive() / onInactive() 回调解决的问题资源管理。场景监听 GPS 位置时不希望界面不可见还在后台耗电。原理LiveData 维护活跃观察者计数器mActiveCount从 0→1 时调用onActive()开始监听 GPS从 1→0 时调用onInactive()停止监听节省资源四、经典陷阱postValue 和 setValue 的赛跑问题liveData.postValue(a) liveData.setValue(b) // 最终值是 a原因setValue 同步postValue 异步执行流程postValue(a)创建一个 Runnable任务是setValue(a)放入主线程消息队列尾部然后立即返回setValue(b)同步执行LiveData 的值立刻变成 b当前代码块执行完毕Looper 取出队列中的 Runnable执行setValue(a)值从 b 被覆盖成 a结论setValue像插队在当前代码块里立即执行postValue只是拿了个排队号要等同步代码执行完才轮到它。多次 postValue 的合并优化liveData.postValue(a) liveData.postValue(b) liveData.postValue(c) // 只有 c 会被分发源码原理LiveData.javavolatile Object mPendingData NOT_SET; private final Runnable mPostValueRunnable new Runnable() { Override public void run() { Object newValue; synchronized (mDataLock) { newValue mPendingData; mPendingData NOT_SET; // 执行后重置标记 } setValue((T) newValue); } }; protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { postTask mPendingData NOT_SET; mPendingData value; // 无论如何都用新值覆盖 } if (!postTask) return; // 已有任务在排队直接返回 ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); }核心mPendingData同时承担存储待更新数据和判断是否有任务排队两个角色。如果已有 Runnable 在排队新的 postValue 只覆盖mPendingData不重复 post。五、粘性事件原理与解决方案什么是粘性事件观察者注册后会立即收到LiveData 当前持有的最新数据即使这份数据是在注册之前发送的。原理在activeStateChanged中if (mActive) { dispatchingValue(this); // 立即触发一次数据分发 }新观察者的mLastVersion初始值是 -1而 LiveData 的mVersion更大版本检查通过onChanged被调用。总结粘性源于观察者从非活跃变为活跃时立即分发最新数据这一设计。它确保 UI 与最新状态同步但产生了粘性副作用。解决方案方案说明SingleLiveEvent继承 LiveData用 AtomicBoolean 标记事件是否已消费官方推荐Event 包装类LiveData 存储EventT内部有hasBeenHandled标记SharedFlow/Channel现代 Android 开发中处理一次性事件的更好选择六、进阶知识点1. MediatorLiveData合并多个数据源MediatorLiveData是 LiveData 的子类可以同时观察多个 LiveData 数据源并在任一数据源变化时触发回调。它常用于合并多个数据源、根据条件动态添加或移除数据源等场景。MediatorLiveDataString mediator new MediatorLiveData(); LiveDataString source1 ...; LiveDataString source2 ...; mediator.addSource(source1, value - mediator.setValue(source1: value)); mediator.addSource(source2, value - mediator.setValue(source2: value));注意当某个数据源不再需要时应调用removeSource()移除避免内存泄漏。2. Transformations数据转换与映射Transformations提供两个常用方法用于对 LiveData 进行声明式转换map()对 LiveData 的值做同步转换返回新的 LiveDataswitchMap()根据源 LiveData 的值动态切换到一个新的 LiveData常用于依赖参数变化的请求// map把 User 对象映射为用户名 LiveDataString userName Transformations.map(userLiveData, user - user.getName()); // switchMap根据 userId 动态切换数据源 LiveDataUser user Transformations.switchMap(userIdLiveData, id - repository.getUser(id));注意Transformations的转换是惰性的只有在有活跃观察者时才会执行这有助于节省资源。3. 与 ViewModel 配合使用LiveData 最常见的实践是放在ViewModel中由 ViewModel 持有数据并通过 LiveData 暴露给 UI。这样做的核心好处是配置变更如屏幕旋转时数据不丢失因为 ViewModel 在 Activity/Fragment 重建后仍然存活。public class UserViewModel extends ViewModel { private final MutableLiveDataUser userLiveData new MutableLiveData(); public LiveDataUser getUser() { return userLiveData; } public void loadUser(String id) { // 异步加载后调用 userLiveData.setValue(user); } }最佳实践对外暴露不可变的LiveData内部使用MutableLiveData修改数据避免外部直接篡改。4. LiveData 与协程 / Flow 的对比在 Kotlin 项目中Flow和StateFlow逐渐成为 LiveData 的替代方案。它们的核心区别维度LiveDataStateFlow / Flow生命周期感知内置自动管理需要借助repeatOnLifecycle手动处理线程切换主线程分发需自行处理天然支持协程flowOn灵活切换粘性行为默认粘性StateFlow 粘性普通 Flow 不粘性数据变换Transformations丰富的操作符map、filter、flatMap 等结论LiveData 简单易用、生命周期感知开箱即用适合中小型项目Flow 功能更强大、更灵活适合复杂异步场景和 Kotlin 优先的新项目。七、常见问题与排查问题 1数据倒灌粘性事件现象页面刚打开或从后台恢复时观察者立即收到一条旧数据导致 UI 被错误刷新例如弹窗重复弹出、列表被重置。排查思路确认是否在onCreate中调用observe()此时 LiveData 已持有旧值新观察者会立即收到一次分发。检查数据是否属于「一次性事件」如 Toast、导航、弹窗这类数据不应使用普通 LiveData 承载。确认是否在 Activity 重建如屏幕旋转后重新订阅导致再次触发粘性分发。解决方案// 方案一Event 包装类标记事件是否已消费 public class EventT { private final T content; private boolean hasBeenHandled; public Event(T content) { this.content content; } public T getContentIfNotHandled() { if (hasBeenHandled) return null; hasBeenHandled true; return content; } } // 使用LiveDataEventString观察时调用 getContentIfNotHandled()若使用 Kotlin推荐改用SharedFlow并配置extraBufferCapacity 0或使用Channel从根源上避免粘性分发。问题 2内存泄漏现象Activity/Fragment 销毁后观察者仍被持有导致页面无法被回收出现内存泄漏。排查思路检查是否使用了observeForever()且未在onDestroy中调用removeObserver()。检查是否在非 LifecycleOwner如自定义 View、工具类中直接持有 LiveData 的引用。检查MediatorLiveData的addSource()是否在页面销毁后仍保留数据源引用。解决方案// 错误observeForever 未移除导致泄漏 liveData.observeForever(observer); // 正确在 onDestroy 中移除 Override protected void onDestroy() { super.onDestroy(); liveData.removeObserver(observer); } // 推荐使用 observe() 绑定 LifecycleOwner自动管理订阅 liveData.observe(this, observer);最佳实践优先使用observe()并传入 LifecycleOwner让 LiveData 自动处理订阅与解绑仅在确实需要全局监听时使用observeForever()并务必在合适的时机手动移除。总结LiveData 的核心价值在于生命周期感知和自动资源管理。理解它的关键机制LifecycleBoundObserver是连接观察者和生命周期的胶水版本号机制防止重复通知和数据倒灌setValue 同步、postValue 异步是很多陷阱的根源粘性事件是设计带来的副作用需要根据场景选择处理方案