Android Livedata源码解析
文章目录
- 前言
- 一、LiveData 简介
- 1. 什么是 LiveData?
- 2. LiveData 的特点和优势
- 特点
- 优势 ( 适用场景 )
- 二、LiveData 的架构设计
- 三、源码解析
- 1. 基本使用
- 2. observe() 的内部逻辑:观察者绑定
- 3. 数据更新:setValue() 和 postValue() 的区别
- 4. 数据分发机制:dispatchingValue() 和 considerNotify()
- 5. 生命周期感知:LifecycleBoundObserver 的实现
- 四、LiveData"粘包"
- 1. 多线程并发更新
- 2. 重复发送数据事件
- 3. 高频数据更新
- 4. 生命周期管理不当
- 解决方案
- 1. 使用 Mutex 锁来保证线程安全:
- 2. 使用 SingleLiveEvent 来避免重复发送:
- 3. 使用 Flow 来控制更新频率:
- 总结
前言
LiveData 的设计初衷,强调其生命周期感知、简化UI组件的数据更新和内存管理,适用于ViewModel数据共享的场景。
下面首先我会先介绍LiveData是什么,然后再介绍源码部分。 想看源码的可以直接跳到源码解析部分
一、LiveData 简介
1. 什么是 LiveData?
LiveData 是一个具有生命周期感知的数据持有类,可帮助 Android UI 组件在数据变化时自动更新。说明它是如何依赖 Android 架构组件库中的 ViewModel 和 Lifecycle,实现数据共享和观察者管理。
2. LiveData 的特点和优势
特点
-
生命周期感知:LiveData 自动根据观察者(如 Activity 或
Fragment)的生命周期进行管理,仅在界面活跃时通知数据更新,避免不必要的 UI 操作和内存泄漏。 -
数据持有类:LiveData 本身是一个数据持有类,能够存储数据。可以在 ViewModel 中创建一个 LiveData 实例来持有数据,使得数据能在配置变更时保持不变。
-
自动更新 UI(与DataBinding 配合使用):当 LiveData 数据发生变化时,它会自动通知注册的观察者更新 UI,减少手动调用 UI 更新逻辑的需求。
-
线程安全:LiveData 内部处理了线程同步,确保在主线程中更新 UI 时不会遇到线程安全问题。
优势 ( 适用场景 )
-
减少内存泄漏:LiveData 能够观察 UI 组件的生命周期,仅在组件活跃状态下更新数据,降低了因生命周期不当导致的内存泄漏风险。
-
简化 UI 控制逻辑:LiveData 自动管理数据状态变化,使得 UI 与数据同步变得轻松,代码逻辑更清晰,易于维护。
-
应对配置变更:与 ViewModel 配合使用,LiveData 能在配置变更时(如旋转屏幕)保存数据状态,避免重新加载和数据丢失的问题。
-
实时数据响应:LiveData 可以直接连接到数据库(例如 Room),实现数据实时响应,数据库更新时自动刷新UI,不需手动监听数据库变化。
-
线程支持:LiveData 提供 postValue 和setValue,可以分别在子线程和主线程中更新数据,帮助在异步任务完成后切换到主线程更新 UI。
二、LiveData 的架构设计
LiveData 的架构设计围绕着生命周期感知、数据订阅发布机制以及数据持有进行设计,旨在简化 UI 层的数据管理,确保数据在不同生命周期阶段的合理更新。
分析源码前 我们先看看LiveData的UML类图:
注:如果不清楚UML关系类图的可以看看下面链接,欢迎大佬们莅临指导
链接: UML类图绘制规则
类图详细说明
-
LifecycleOwner
关系:与 Lifecycle 是关联关系,通过 getLifecycle() 方法获取 Lifecycle 对象。
功能:表示具备生命周期的组件,提供 getLifecycle() 方法以获取其生命周期状态,允许 LiveData 自动管理观察者的活跃状态。 -
Lifecycle
关系:LifecycleOwner 拥有 Lifecycle;Lifecycle 聚合 LifecycleObserver。
功能:管理生命周期事件并通知观察者,通过 addObserver() 和 removeObserver() 方法添加或移除观察者。getCurrentState() 方法提供当前的生命周期状态。 -
LifecycleObserver
关系:LiveData 通过 LifecycleBoundObserver 实现 LifecycleObserver,用来监听 Lifecycle 的状态变化。
功能:用于观察 Lifecycle 的状态,在 LiveData 中自动管理观察者的活跃性。LifecycleObserver 可以跟踪 LifecycleOwner 的生命周期事件,自动激活和暂停观察者。 -
LiveData
关系:与 LifecycleObserver 是组合关系:LiveData 使用内部类 LifecycleBoundObserver 管理 LifecycleOwner 的状态。与 Observer 是依赖关系:LiveData 依赖 Observer,通过 observe() 注册观察者。
功能:提供观察数据的功能,当数据变化时通知观察者。包括 observe() 方法绑定带有 LifecycleOwner 的观察者,observeForever() 方法绑定无生命周期的观察者。 -
LifecycleBoundObserver
关系:LifecycleBoundObserver 是 LiveData 的内部类,继承自 LifecycleObserver。
功能:LifecycleBoundObserver 将 Observer 与特定的 LifecycleOwner 绑定,使得 LiveData 根据 Lifecycle 状态控制观察者的激活或暂停。 -
Observer
关系:与 LiveData 是依赖关系,通过 onChanged() 方法响应数据的变化。
功能:Observer 定义了 onChanged() 方法,当 LiveData 中的数据更新时被调用,用于响应数据的变化。 -
MutableLiveData
关系:继承自 LiveData,是一种可变的 LiveData。
功能:通过 setValue() 和 postValue() 方法,允许外部更新数据。 -
MediatorLiveData
关系:继承自 LiveData,并且组合多个 LiveData 数据源。
功能:通过 addSource() 和 removeSource() 方法,可以动态添加或移除 LiveData 数据源。MediatorLiveData 会监听所有数据源的变化,并在指定条件下触发观察者的更新。
UML类图看完了,那我们看看他们大致的时序图
看不懂图没关系 我们看一下文字描述,图文结合就能懂了
注:在 LiveData 的上下文中,“if in active state” 表示观察者(Observer)处于“活跃状态”时,LiveData 会将数据通知给该观察者。这个活跃状态(active state)是由 LifecycleOwner 的生命周期状态决定的。
活跃状态的定义
跃状态(Active State): 在 LiveData 中,观察者会被标记为活跃,当它的 LifecycleOwner 处于 STARTED 或 RESUMED 状态时(比如 Activity 正在前台运行,或 Fragment 正在显示)。只有当观察者处于活跃状态时,它才会接收到 LiveData 的数据更新。
非活跃状态(Inactive State): 如果 LifecycleOwner 的状态低于 STARTED(如 CREATED 或 DESTROYED),则此时的观察者被视为“非活跃状态”。非活跃状态下,LiveData 不会通知观察者数据更新,以节省资源。
时序图的详细流程:
1. 观察者注册(调用 observe() 方法)
-
Activity/Fragment(或其他 LifecycleOwner)调用 LiveData.observe(),传入
LifecycleOwner 和 Observer。 -
LiveData 检查 LifecycleOwner 的生命周期状态:
1、如果状态为 STARTED 或 RESUMED,则标记 Observer 为活跃状态,并立即触发通知,将当前数据推送给该观察者。
如果状态低于 STARTED,则不会立即通知数据,等待状态更新到活跃时再通知。
2、LiveData 将该 Observer 添加到观察者列表中,监听其生命周期变化。
2. 生命周期状态改变
- 当 LifecycleOwner 的生命周期状态变化(如从 CREATED 到 STARTED),LiveData 会监听到该事件。
- LiveData 再次检查 LifecycleOwner 的状态:
1、如果新的状态为 STARTED 或 RESUMED,LiveData 将此观察者标记为活跃,并触发数据通知(如果有新数据)。
2、如果状态低于 STARTED(例如 DESTROYED),LiveData 将停止对该观察者的更新,可能会从观察者列表中移除该观察者。
3. 数据更新(调用 setValue() 或 postValue())
- 当调用 setValue() 或 postValue() 更新 LiveData 的数据时:
1、 LiveData 将数据标记为“已更新”状态,准备通知所有活跃的观察者。
2、 如果当前没有活跃的观察者,LiveData 将暂存新数据,等待后续观察者变为活跃时再通知。
3、如果有活跃的观察者,则将最新数据立即推送给所有活跃的观察者。
4. 数据通知
1、LiveData 会遍历所有活跃状态的观察者,将最新数据推送给每个观察者。
2、Observer 的 onChanged() 方法被调用,以接收最新的数据并触发 UI 更新。
5. 观察者移除
当 LifecycleOwner 的生命周期状态到达 DESTROYED 状态时,LiveData 自动从观察者列表中移除该观察者。
这一步确保不再对已销毁的 LifecycleOwner 进行数据通知,防止内存泄漏。
三、源码解析
了解了LiveData大致工作原理后 下面我们从源码角度开始分析
1. 基本使用
在实际项目中,LiveData 通常配合 ViewModel 使用。下面是 LiveData 的基本使用步骤:
在 ViewModel 中定义 LiveData,并通过 setValue() 或 postValue() 更新数据。
在 Activity 或 Fragment 中通过 observe() 注册观察者,接收 LiveData 的数据更新。
// MyViewModel.kt
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
fun updateData(newData: String) {
_data.value = newData
}
}
// MyActivity.kt
class MyActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
viewModel.data.observe(this, Observer { data ->
// 数据更新时调用
println("Updated data: $data")
})
}
}
从上述代码可以看出,observe() 方法会将一个 Observer 附加到 LiveData 上,从而监听数据的变化。接下来,我们深入探究 LiveData 是如何管理这些观察者,并如何实现数据分发的。
2. observe() 的内部逻辑:观察者绑定
从源码角度看,observe() 方法在接收 LifecycleOwner 和 Observer 后,主要执行以下几个步骤:
1、检查当前调用是否在主线程。
2、将 Observer 包装成 ObserverWrapper,并将其与 LifecycleOwner 关联。
判断 LifecycleOwner 的当前状态是否为活跃状态,如果是则立即接收数据更新。
observe() 方法源码
// LiveData.java
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe"); // 确保在主线程中调用
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && existing.isAttachedTo(owner)) {
return; // 如果已经在该 LifecycleOwner 上注册,直接返回
}
// 确保不能重复注册相同的 observer
if (existing != null) {
throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
}
// 将 wrapper 添加为 LifecycleOwner 的生命周期观察者
owner.getLifecycle().addObserver(wrapper);
}
LifecycleBoundObserver 是 LiveData 内部的包装类,用于管理 Observer 的生命周期。
LifecycleOwner 会监听其生命周期的变化,当状态处于活跃(STARTED 或 RESUMED)时,Observer 会被激活,从而能够接收数据更新。
3. 数据更新:setValue() 和 postValue() 的区别
在实际开发中,数据更新有同步和异步两种方式:
1、setValue():在主线程直接更新数据。
2、postValue():在非主线程更新数据,利用 Handler 切换到主线程分发。
setValue() 源码解析
当我们在主线程调用 setValue() 时,LiveData 会立即更新 mData,并触发数据分发。
protected void setValue(T value) {
assertMainThread("setValue"); // 确保在主线程中调用
mVersion++; // 增加版本号,表示数据已更新
mData = value; // 更新数据
dispatchingValue(null); // 开始数据分发
}
mVersion 表示数据的版本号,每次更新数据时递增。
dispatchingValue(null) 会检查当前的 Observer 是否处于活跃状态,并将最新数据推送给所有活跃的观察者。
postValue() 源码解析
对于非主线程的调用,我们会使用 postValue() 来更新数据:
public void postValue(T value) {
boolean postTask;
synchronized (mDataLock) { //加锁 确保线程安全
postTask = mPendingData == NOT_SET; // 检查是否已经有待更新的数据
mPendingData = value; // 设置待更新的数据
}
// 如果没有待更新的数据,直接返回
if (!postTask) {
return;
}
// 切换到主线程执行更新
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
postValue() 将新数据暂存到 mPendingData,并通过 ArchTaskExecutor 切换到主线程,最终由 mPostValueRunnable 调用 setValue() 完成数据更新。
4. 数据分发机制:dispatchingValue() 和 considerNotify()
当调用 setValue() 或 postValue() 时,LiveData 会进入数据分发流程。dispatchingValue() 是数据分发的核心方法。
dispatchingValue() 方法源码
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) { // 检查是否正在分发
mDispatchInvalidated = true; // 如果正在分发,则标记为无效
return;
}
mDispatchingValue = true; // 标记为正在分发
do {
mDispatchInvalidated = false; // 重置无效标记
if (initiator != null) {
// 如果有 initiator,优先通知该 observer
considerNotify(initiator);
initiator = null; // 清空 initiator
} else {
// 遍历所有观察者
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions();
iterator.hasNext(); ) {
considerNotify(iterator.next().getValue()); // 通知每个活跃的 observer
if (mDispatchInvalidated) { // 如果分发状态变为无效,则中断
break;
break;
}
}
}
} while (mDispatchInvalidated); // 如果有无效标记,继续分发
mDispatchingValue = false; // 标记为不再分发
}
该方法会遍历 mObservers,调用 considerNotify() 向每个符合条件的观察者分发数据。
mDispatchingValue 和 mDispatchInvalidated 用于控制分发状态,避免重复分发。
considerNotify() 方法源码
considerNotify() 判断当前 Observer 是否活跃且需要更新,并调用 onChanged() 向其推送数据。
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) { // 检查 observer 是否处于活跃状态
return; // 如果不活跃,直接返回
}
// 检查 observer 是否应该活跃
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false); // 如果不应活跃,则更新状态
return;
}
// 检查 observer 是否已经接收到最新版本的数据
if (observer.mLastVersion >= mVersion) {
return; // 如果是最新数据,直接返回
}
observer.mLastVersion = mVersion; // 更新上次版本号
observer.mObserver.onChanged((T) mData); // 触发 observer 的数据更新
}
- shouldBeActive() 判断 Observer 是否符合分发条件。
- 若 Observer 活跃且未接收到最新版本的数据,则调用 onChanged() 传递数据更新。
5. 生命周期感知:LifecycleBoundObserver 的实现
LifecycleBoundObserver 是 LiveData 的内部包装类,它实现了 LifecycleEventObserver,从而使 Observer 能够感知生命周期的变化并自动调整活跃状态。
private class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull final LifecycleOwner mOwner; // 绑定的 LifecycleOwner
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer); // 调用父类构造器
mOwner = owner; // 记录 LifecycleOwner
}
@Override
boolean shouldBeActive() {
// 判断当前 LifecycleOwner 的状态是否处于活跃状态
return mOwner.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
// 监听 LifecycleOwner 状态变化
if (mOwner.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
removeObserver(mObserver); // 如果已销毁,移除 observer
return;
}
// 根据当前状态更新活跃状态
activeStateChanged(shouldBeActive());
}
}
shouldBeActive() 方法会根据 LifecycleOwner 的当前状态判断 Observer 是否应保持活跃。
onStateChanged() 方法在生命周期状态改变时调用,用于移除已销毁的 Observer,或在 Observer 变为活跃时分发数据。
四、LiveData"粘包"
什么是LiveData“粘包”问题?
粘包指的是数据流在传输过程中被多个数据块粘合在一起,导致原本独立的数据包无法正常解析。这种现象在网络请求、并发数据处理时常见,而在使用 LiveData 时,由于线程不安全或者数据合并不当,也可能导致类似的“粘连”现象。
1. 多线程并发更新
比喻: 服务员使用托盘承载多个订单,但如果多个顾客同时下单,新的订单会覆盖旧的。
示例代码
val liveData = MutableLiveData<String>()
// 模拟两个线程同时更新
Thread {
liveData.postValue("Order from Thread 1")
}.start()
Thread {
liveData.postValue("Order from Thread 2")
}.start()
服务员:postValue 方法。
托盘:mPendingData。
咖啡杯:更新的数据。
由于两个线程几乎同时更新,最终可能只接收到 “Order from Thread 2” 的数据,而 “Order from Thread 1” 被覆盖掉了。
2. 重复发送数据事件
比喻: 广播员在每次广告结束后重复播放上一段音乐。
示例代码:
val liveData = MutableLiveData<String>().apply { value = "Initial Data" }
liveData.observe(this) { data ->
println("Received: $data")
}
// 模拟 Activity 重建
liveData.value = "Updated Data"
形象解释
广播员:LiveData 的观察者。
音乐:数据更新。
在重建时,观察者再次接收到 “Updated Data”,造成多次重复通知。
3. 高频数据更新
比喻: 传送带上的产品不断变化,但你只能抓到最新的一个。
示例代码:
fun fetchDataStream(): LiveData<String> = liveData {
for (i in 1..10) {
emit("Data $i") // 数据流快速更新
delay(100) // 模拟快速更新
}
}
// 观察数据
fetchDataStream().observe(this) { data ->
println("Received: $data")
}
形象解释
传送带:数据流。
产品:数据更新。
如果 fetchDataStream 运行速度很快,观察者可能只会接收到最后几次的数据,而中间的数据可能会被“丢掉”。
4. 生命周期管理不当
比喻: 图书馆重启时,借书系统导致重复借到同一本书。
示例代码
val liveData = MutableLiveData<String>()
// 观察者
liveData.observe(this) { data ->
println("Received: $data")
}
// 模拟 Activity 重建
liveData.value = "New Data"
形象解释
图书馆:LiveData。
书籍:数据。
在 Activity 重建时,观察者接收到了相同的数据更新,造成冗余的通知。
解决方案
1. 使用 Mutex 锁来保证线程安全:
val mutex = Mutex()
fun safePostValue(data: String) {
CoroutineScope(Dispatchers.IO).launch {
mutex.withLock {
liveData.postValue(data)
}
}
}
2. 使用 SingleLiveEvent 来避免重复发送:
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner) { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
}
override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
}
3. 使用 Flow 来控制更新频率:
fun fetchDataFlow(): LiveData<String> = liveData {
flow {
for (i in 1..10) {
emit("Data $i")
delay(100) // 控制更新频率
}
}.collect { data ->
emit(data)
}
}
总结
通过源码解析可以看出,LiveData 的核心设计基于观察者模式,结合 Lifecycle 实现生命周期感知的数据观察与分发。observe()、setValue() 和 dispatchingValue() 各司其职,确保数据在适当的生命周期内分发给活跃的观察者,提升了 Android 应用的数据管理能力和开发体验。