LiveData源码分析2 -- 原理分析

907 阅读9分钟

「这是我参与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包中有什么新扩展之类的,这些内容我们下篇文章继续分析。