LiveData源码分析2 -- 原理分析
前言
本章直接通过上面一章所介绍的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包中有什么新扩展之类的,这些内容我们下篇文章继续分析。
作者:元浩875
链接:https://juejin.cn/post/7054384684536594446