当前位置: 首页 > news >正文

Android LiveData学习总结(源码级理解)

LiveData 工作原理

  • 数据持有与观察者管理LiveData 内部维护着一个数据对象和一个观察者列表。当调用 observe 方法注册观察者时,会将 LifecycleOwner 和 Observer 包装成 LifecycleBoundObserver 对象并添加到观察者列表中。
  • 生命周期感知LifecycleBoundObserver 实现了 LifecycleEventObserver 接口,能够监听 LifecycleOwner 的生命周期变化。当 LifecycleOwner 进入活跃状态(STARTED 或 RESUMED)时,LiveData 会将最新数据发送给该观察者;当 LifecycleOwner 进入销毁状态(DESTROYED)时,LiveData 会自动移除该观察者,避免内存泄漏。
  • 数据更新通知:当调用 setValue(主线程)或 postValue(子线程)方法更新数据时,LiveData 会检查所有观察者的生命周期状态,只有处于活跃状态的观察者才会收到 onChanged 方法的调用,从而更新 UI。

整体架构与核心类

LiveData 相关的核心类主要有 LiveDataObserverLifecycleOwner 和 Lifecycle

  • LiveData:数据持有者类,负责存储数据并通知观察者数据的变化。
  • Observer:观察者接口,定义了数据变化时的回调方法。
  • LifecycleOwner:具有生命周期的组件,如 ActivityFragment,实现了该接口。
  • Lifecycle:用于跟踪组件的生命周期状态。

工作流程与源码解析

1. 创建 LiveData 对象
LiveData<String> liveData = new MutableLiveData<>();

MutableLiveData 是 LiveData 的子类,它提供了 setValue() 和 postValue() 方法来更新数据。

2. 注册观察者
liveData.observe(this, new Observer<String>() {@Overridepublic void onChanged(String s) {// 数据变化时的回调}
});

下面是 observe() 方法的源码:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");if (owner.getLifecycle().getCurrentState() == DESTROYED) {// 如果 LifecycleOwner 已经销毁,直接返回return;}LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);if (existing != null && !existing.isAttachedTo(owner)) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}owner.getLifecycle().addObserver(wrapper);
}
  • observe() 方法首先检查是否在主线程中调用,然后检查 LifecycleOwner 的状态,如果已经销毁则直接返回。
  • 创建 LifecycleBoundObserver 对象,它是 ObserverWrapper 的子类,实现了 LifecycleEventObserver 接口,用于监听 LifecycleOwner 的生命周期变化。
  • 将 observer 和 LifecycleBoundObserver 包装对象存入 mObservers 集合中。
  • 最后将 LifecycleBoundObserver 注册到 LifecycleOwner 的生命周期观察者列表中。
3. 更新数据

可以使用 setValue() 或 postValue() 方法更新 LiveData 中的数据。

// 在主线程中更新数据
((MutableLiveData<String>) liveData).setValue("new value");// 在子线程中更新数据
((MutableLiveData<String>) liveData).postValue("new value");

setValue() 方法的源码:

@MainThread
protected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);
}
  • setValue() 方法首先检查是否在主线程中调用,然后更新数据的版本号和数据值。
  • 调用 dispatchingValue() 方法通知所有观察者数据发生了变化。

postValue() 方法的源码:

protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
  • postValue() 方法用于在子线程中更新数据,它会将数据存入 mPendingData 中,并通过 ArchTaskExecutor 将更新操作切换到主线程中执行。
4. 通知观察者

dispatchingValue() 方法用于通知观察者数据发生了变化:

void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue = false;
}
  • dispatchingValue() 方法会遍历所有的观察者,并调用 considerNotify() 方法通知每个观察者。

considerNotify() 方法的源码:

private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;//noinspection unchecked((Observer<T>) observer.mObserver).onChanged((T) mData);
}
  • considerNotify() 方法会检查观察者的活跃状态和数据版本号,如果观察者不活跃或数据版本号没有变化,则不进行通知。
  • 如果满足条件,则调用观察者的 onChanged() 方法,将最新的数据传递给观察者。

扩展追问

LiveData 的 postValue 方法用于在后台线程中更新 LiveData 的值。不过,使用这个方法时可能会出现值丢失的情况,下面结合源码深入分析其原因。

postValue 方法源码分析

postValue 方法的实现位于 LiveData 类中,以下是 postValue 方法的源码:

protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}private final Runnable mPostValueRunnable = new Runnable() {@Overridepublic void run() {Object newValue;synchronized (mDataLock) {newValue = mPendingData;mPendingData = NOT_SET;}setValue((T) newValue);}
};

值可能丢失的原因

1. 多线程并发调用 postValue

postValue 方法会将新值存储在 mPendingData 中,并通过 ArchTaskExecutor 将一个 Runnable 任务(mPostValueRunnable)发送到主线程执行。当在多线程环境下频繁调用 postValue 方法时,可能会出现值丢失的情况。

具体来说,postValue 方法中有一个同步块:

synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;
}

在这个同步块中,会判断 mPendingData 是否为 NOT_SET(表示没有待处理的值)。如果不是 NOT_SET,则 postTask 为 false,不会再次发送 Runnable 任务到主线程。这意味着,如果在 Runnable 任务还未执行时,又有新的 postValue 调用,新的值会覆盖 mPendingData 中的旧值,而旧值就会丢失。

例如,假设在后台线程中有两个线程同时调用 postValue 方法:

// 线程 1
liveData.postValue("value1");
// 线程 2
liveData.postValue("value2");

如果线程 2 在 mPostValueRunnable 还未执行时就调用了 postValue 方法,那么 mPendingData 中的值会从 "value1" 被覆盖为 "value2",最终 mPostValueRunnable 执行时,setValue 方法只会将 "value2" 发送给观察者,"value1" 就丢失了。

2. 主线程任务队列的延迟

postValue 方法会将 mPostValueRunnable 任务发送到主线程执行,而主线程有自己的任务队列。如果主线程比较繁忙,mPostValueRunnable 任务可能会延迟执行。在这个延迟期间,如果有新的 postValue 调用,同样会导致值丢失。

例如,当主线程正在处理大量的 UI 绘制任务时,mPostValueRunnable 任务可能会被阻塞在队列中。此时,如果有新的 postValue 调用,mPendingData 中的值会被更新,旧的值就会丢失。

解决方案

如果需要确保每个值都能被处理,可以使用 setValue 方法,但 setValue 方法必须在主线程中调用。如果需要在后台线程中更新值,可以考虑使用 MutableLiveData 的子类,自定义实现更新逻辑,确保每个值都能被正确处理。

// 在主线程中使用 setValue 方法更新值
liveData.setValue("newValue");

综上所述,LiveData 的 postValue 方法由于多线程并发调用和主线程任务队列的延迟,可能会导致值丢失。在使用时需要根据具体情况选择合适的更新方法。

总结

LiveData 的工作原理主要基于观察者模式和生命周期感知机制。

通过 observe() 方法注册观察者,将观察者与 LifecycleOwner 关联起来,当 LifecycleOwner 的生命周期状态发生变化时,LiveData 会自动处理观察者的活跃状态。

当数据更新时,LiveData 会通知所有活跃的观察者,确保只有在组件处于活跃状态时才更新 UI。这种设计使得 LiveData 具有良好的内存管理和用户体验。

相关文章:

  • (小白0基础) 微调deepseek-8b模型参数详解以及全流程——训练篇
  • Spark-sql编程
  • (二)Graspnet在mujoco的仿真复现(操作记录)
  • 天津大学 | 智能制造与数字孪生技术:面向可持续制造方向发展
  • 在Centos7下源码安装部署 MySQL57
  • 【C语言基础】双指针在qsort函数中的应用
  • 浙江大学:DeepSeek如何引领智慧医疗的革新之路?|48页PPT下载方法
  • WordPress - 此站点出现严重错误
  • 《AI大模型应知应会100篇》第19篇:隐私保护与大模型训练
  • 计算机网络 - 四次挥手相关问题
  • 利用IDEA开发Spark-SQL
  • 状态机编程中的事件和状态
  • nginx自编译重现gzip和chunked的现象
  • MATLAB程序实现了一个物流配送优化系统,主要功能是通过遗传算法结合四种不同的配送策略,优化快递订单的配送方案
  • 封装实用的时间选择器组件
  • YOLOv3超详细解读(三):源码解析:数据处理模块
  • Spring Boot 参数校验 Validation 终极指南
  • Open AI 使用篇
  • 从拥堵到畅行,智慧城市如何实现交通魔法?
  • 深入剖析Java中ThreadLocal原理
  • 沙龙 | 新书分享:中国电商崛起的制度密码
  • 云南昭通一公园发现毒饵,多只宠物狗疑中毒致死
  • 国际市场开心果价格上涨35%,背后助力是一条点击过亿的短视频
  • 从板凳席到指挥台,横扫广东男篮的少帅潘江究竟有何神奇
  • 第六次国民体质监测展开,高抬腿俯卧撑等新增运动指标受关注
  • 正义网评“一男两女举办婚礼”:“一夫多妻”流量闹剧该歇了