LiveData 工作原理
- 数据持有与观察者管理:
LiveData
内部维护着一个数据对象和一个观察者列表。当调用observe
方法注册观察者时,会将LifecycleOwner
和Observer
包装成LifecycleBoundObserver
对象并添加到观察者列表中。 - 生命周期感知:
LifecycleBoundObserver
实现了LifecycleEventObserver
接口,能够监听LifecycleOwner
的生命周期变化。当LifecycleOwner
进入活跃状态(STARTED
或RESUMED
)时,LiveData
会将最新数据发送给该观察者;当LifecycleOwner
进入销毁状态(DESTROYED
)时,LiveData
会自动移除该观察者,避免内存泄漏。 - 数据更新通知:当调用
setValue
(主线程)或postValue
(子线程)方法更新数据时,LiveData
会检查所有观察者的生命周期状态,只有处于活跃状态的观察者才会收到onChanged
方法的调用,从而更新 UI。
整体架构与核心类
LiveData
相关的核心类主要有 LiveData
、Observer
、LifecycleOwner
和 Lifecycle
。
LiveData
:数据持有者类,负责存储数据并通知观察者数据的变化。Observer
:观察者接口,定义了数据变化时的回调方法。LifecycleOwner
:具有生命周期的组件,如Activity
、Fragment
,实现了该接口。Lifecycle
:用于跟踪组件的生命周期状态。
工作流程与源码解析
1. 创建 LiveData
对象
LiveData<String> liveData = new MutableLiveData<>();
MutableLiveData
是 LiveData
的子类,它提供了 setValue()
和 postValue()
方法来更新数据。
2. 注册观察者
liveData.observe(this, new Observer<String>() {
@Override
public 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() {
@Override
public 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
具有良好的内存管理和用户体验。