Android LiveData学习总结(源码级理解)
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>() {@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 具有良好的内存管理和用户体验。
