「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
前言
本章直接通过上面一章所介绍的LiveData特性,来看一下是如何实现这些特性的。
正文
其实LiveData的核心实现就2个方向,一个是更改其持有的值如何通知到观察者,一个是添加观察者,我们也就从这2方面入手分析。
分发、通知数据变化
先看一下构造函数:
public LiveData(T value) {
//这里使用mData来保存数据
mData = value;
//每操作一次这个数据的version加一
mVersion = START_VERSION + 1;
}
//LiveData保存的字段
//使用Object来保存数据,根据Kotlin的泛型是伪泛型,且被擦除,所以这里用Object来保存T类型数据
//这里使用volatile来修饰mData,所以要求线程之间是可见的,至于多线程同步,后面细说
private volatile Object mData;
//数据版本
private int mVersion;
这里构造函数就能看出,要保证多线程同步,接着看:
//主线程修改值
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
//mData值改变,进行通知分发给观察者
dispatchingValue(null);
}
//分发值
//ObserverWrapper后面再说
void dispatchingValue(@Nullable ObserverWrapper initiator) {
//当正在分发值时,分发使能无效为true
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
//开始分发值
mDispatchingValue = true;
do {
//一旦开始分发,分发使能无效为false
mDispatchInvalidated = false;
//分发给单个观察者
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
//分发给多个观察者
//特殊集和 见分析1
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//关键代码
considerNotify(iterator.next().getValue());
//发现分发使能无效立马停止 见分析2
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
//分发结束
mDispatchingValue = false;
}
分析1:这个集和在前面Lifecycle源码有介绍过,它是一个链表形成的map,可以查看文章:juejin.cn/post/705294…
分析2:其实这段代码很有参考意义,这里只能在主线程中执行,由于遍历链表是个耗时操作,所以会出现前面一个值还没有分发完,后面一个值就接着来了,这时使用了2个标志位mDispatchingValue和mDispatchInvalidated来控制,当正在遍历时,发现有新的值需要去分发,这时及时打断遍历,再从头遍历,使用新的值来分发。
这里使用不常用的do while循环来完成这个逻辑,同时也展示了一个LiveData特性:当有多个观察者时,且值变化太快,只能通知最新的值。
继续看一下分发代码:
//根据observer来判断是否要通知
private void considerNotify(ObserverWrapper observer) {
//假如observer是非活跃的,就不进行通知
//具体逻辑见分析3
if (!observer.mActive) {
return;
}
//当observer应该是Active为false,则不进行通知
//具体原因见分析4
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//当observer值的版本已经比现在要分发的值更新,则不用通知
if (observer.mLastVersion >= mVersion) {
return;
}
//更新值版本
observer.mLastVersion = mVersion;
//进行通知
observer.mObserver.onChanged((T) mData);
}
这里也就是LiveData的又一个特性:当观察者对应的Lifecycle处于不活跃时,将不会通知。
分析3:那就需要看一下这里是如何判断是否活跃的,这里也就是ObserverWrapper类的实现:
//抽象的父类
private abstract class ObserverWrapper {
//观察者
final Observer<? super T> mObserver;
//是否活跃
boolean mActive;
//数据版本
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
//子类实现
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
//是否活跃状态变化
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
//修改是否活跃
mActive = newActive;
//判断活跃的观察者数量 见分析5
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
//当是活跃时分发事件
dispatchingValue(this);
}
}
}
先继续分析3的来说,这里是如何判断是否还是活跃,就看其实现类了:
//实现类,且实现了LifecycleEventObserver接口,用来接收Lifecycle的事件
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
//生命周期持有类,也就是Activity/Fragment
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
//当Lifecycle的State在STARTED以及以上才是活跃
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
//Lifecycle的事件变化
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//当前Lifecycle的状态
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
//当是DESTROYED时移除观察者
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
//active状态发生变化
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
由上面代码的onStateChanged可以看出LiveData的又一个重要特性:当观察者对应的Lifecycle的生命周期状态是DESTROYED时将自动移除该观察者。
同时会调用父类的activityStateChanged方法来设置活跃程度,也就是一般而言生命周期在STARTED以及以后的值。
分析5:在抽象类的activeStateChange代码中有个关键地方,当mActive为true时,他会去分发值,然而这个方法的调用会在生命周期变化时调用,所以可以得到LiveData的一个重要特性:当观察者对应的生命周期发生变化时,由不活跃变成活跃时,将通知分发LiveData保存的值,这也就是当界面重新可见时会自动刷新在可见之前保存的值。
分析5部分代码还调用了一个判断活跃观察者数量的方法:
//判断活跃观察者数量
void changeActiveCounter(int change) {
int previousActiveCount = mActiveCount;
mActiveCount += change;
if (mChangingActiveState) {
return;
}
mChangingActiveState = true;
try {
while (previousActiveCount != mActiveCount) {
boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
previousActiveCount = mActiveCount;
if (needToCallActive) {
//当有活跃观察者时
onActive();
} else if (needToCallInactive) {
//当没有活跃观察者时
onInactive();
}
}
} finally {
mChangingActiveState = false;
}
}
这里的onActive和onInactive是LiveData的方法,这个在后面自定义LiveData时有关键作用,可以释放一些资源。
上面其实是分析3的衍生,说明了如何判断观察者是否活跃以及实现方法,继续看一下分析4处的代码,在 considerNotify注释中。
分析4:既然可以通过mActive可以判断是否活跃,为什么还要调用一次shouldBeActive呢,原因非常简单在上面我们分析过observer在收到Lifecycle的状态变化时才去更新mActive,但是当Event还没有获取到时,这时就有值需要去分发,这时就要手动调用这个方法去判断,进一步确保代码正确性。
子线程修改数据
上面代码说了主线程修改数据、通知观察者以及判断观察者是否活跃的流程,里面会发现有很多特性,接着我们来看一下子线程修改数据以及多线程数据同步如何实现。
//子线程调用
protected void postValue(T value) {
//处理快速修改情况 见分析1
boolean postTask;
synchronized (mDataLock) {
//上锁,mPendingData保存value 见分析2
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
//主线程执行runnable
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
//锁
final Object mDataLock = new Object();
//volatile修饰的变量
volatile Object mPendingData = NOT_SET;
//主线程运行的逻辑
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
//锁住同一个对象 见分析2
newValue = mPendingData;
mPendingData = NOT_SET;
}
//主线程分发数据
setValue((T) newValue);
}
};
//主线程分发值,上面已经说过
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
这里可以发现它实现多线程同步的方式有一点不一样,并没有把某个 setValue(假设设置值) 的方法设置为同步的,而是仅仅锁了2个代码块,我们来分析一下。
分析2:在主线程的postValue中和和主线程的runnable中都对mdDataLock进行了锁,说明给mPendingData赋值和取出mPendingData是多线程同步的,而这个mPendingData也就负责线程之间的数据传递,所以就达到了线程同步的要求。
但是对于getValue方法也就是获取LiveData的值的方法如下:
//获取保存的值,非同步方法
public T getValue() {
Object data = mData;
if (data != NOT_SET) {
return (T) data;
}
return null;
}
这里却不是个同步方法,原因非常简单:
private volatile Object mData;
mData是线程透明的,它是用volatile修饰的,所以只需要保证多线程赋值是同步的即可,而且实际情况是通过锁一个对象来间接实现赋值同步的,不用同步整个方法,这也是设计的巧妙之处。
分析1:在分析1处有个psotTask标志位,这个标志位十分重要,当发现主线程还没有处理mPnedingData时,将不再向主线程从新发送runnable,这个也非常好理解,因为主线程要干的事非常多,假如该子线程不断的发送值,如果不做处理将不停地给主线程发runnable,而主线程要等待到才会去处理,这就可能导致延迟性。
所以这里又算是LiveData的一个特性:当一个子线程不断的postValue时,在主线程没有处理完之前,只会去分发最新的值。
添加观察者
上面说完了修改值时如何通知观察者,那就还剩一个主要内容便是添加观察者,代码如下:
//主线程
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
//当是DESTROYED状态时,直接无法添加
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//包裹一层,实现了Observer和LifecycleEvent2个接口
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;
}
//给Lifecycle添加观察者
owner.getLifecycle().addObserver(wrapper);
}
添加观察者代码非常简单,核心就是包裹类,把Observer和LifecycleEventObserver给结合起来,把这个wrapper添加给Lifecycle,当Lifecycle生命周期变化时,会通知wrapper,这里代码不用多说了。
到这里,LiveData这个类的代码我们说完了,但是相关知识还没有结束,我们后面继续。
总结
本章内容主要是介绍LiveData的原理,其中包含了很多LiveData的特性实现,都有用加黑字条表示,但是LiveData的知识还不止如此,比如如何转换LiveData、MediatroData如何使用、KTX包中有什么新扩展之类的,这些内容我们下篇文章继续分析。