O_o
O_o
文章目录
  1. 一、构造 LiveData
  2. 二、订阅 LiveData
  3. 三、当 setValue的时候发生了什么
  4. 四、扩展问题
    1. postValue 是如何实现的?
    2. MutableLiveData 和 LiveData 的区别
    3. Transformations.map 和 switchMap
  5. 五、总结

What's inside?-LiveData

距离上一次更新博客已经过去很久了,其实中间也有写一些东西但是没有认真发布到博客上,希望能坚持初心,把博客当作学习记录来激励自己持续学习。

  • 当 LiveData 更新 value 的时候实际发生了什么?
  • LiveData 是如何绑定观察者的生命周期的?
  • 当我们订阅 LiveData 时,为什么只有当前观察者收到数据?

也许在使用 LiveData 时还有其他的问题,而所有问题的答案几乎都清楚的写在源码里。本篇文章中阅读的为androidx.lifecyle:lifecycle-livedata-core:2.2.0 中的 LiveData

一、构造 LiveData

构造 LiveData 有两种方式,一种使用默认构造函数,另一种可以在构造时初始化value的值。构造函数的内容很简单,初始化了mDatamVersion,我们这里先记住这个 mVersion

public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
static final int START_VERSION = -1;

二、订阅 LiveData

订阅通常也有两种方式,一种是observe传入 LifecycleOwner 另一种是observerForever。 先从 observe 开始看:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// 首先检查是否在主线程调用的该函数
assertMainThread("observe");
// 如果传入的 lifecycle owner 的状态是 destoryed,就取消本次订阅
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 使用传入的两个参数构造了一个 LifecycleBoundObserver, 即一个包装类
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 将传入的observer 和刚刚构造的 wrapper 存入到 mObservers 中
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 如果existing 不为空,并且没有绑定到传入的owner上,说明之前已经绑定了另一个生命周期宿主
// 不允许同时绑定多个宿主,抛错误
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
// 到这一步就说明之前已经成功订阅过了,直接返回,不进行后续操作
return;
}
// 将wrapper和宿主的生命周期进行绑定
owner.getLifecycle().addObserver(wrapper);
}

上面的代码逻辑很简单,里面出现了几个我们可能关心的类或变量:mObserversLifecycleBoundObserver

其中 mObserversSafeIterableMap, 实际是个链表假装的 Map,这里不展开。key 是 observer,也就是上面方法中传进来的第二个参数,value 是ObserverWrapper

另一个 LifecycleBoundObserver 就比较关键了,从上面的信息可以推出它是ObserverWrapper 的一个子类。我们先从 owner.getLifecycle().addObserver(wrapper); 这个代码作为入口来看:

// 这里面还有一些 lifecycle 的逻辑,这里先去掉不看
void dispatchEvent(LifecycleOwner owner, Event event) {
// 最终调用到 ObserverWithState 的 dispatchEvent 方法中来
//...
// 调用 lifecycleObserver 的onStateChanged 方法
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
//...
}

既然 addObserver 可以把 wrapper 作为参数传入,那就说明它实现了 LifecycleEventObserver 接口,这里就解释了开头的第二个问题,让我们进入到LifecycleBoundObserver 类中:

public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// 当宿主的生命周期发生改变时或者刚刚绑定时会回调这个方法
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
// 如果宿主的状态时 destroyed, 移除观察者
removeObserver(mObserver);
return;
}
// 关键代码
activeStateChanged(shouldBeActive());
}

先看shouldBeActive()

boolean shouldBeActive() {
// 状态至少要大于等于 STARTED, 进入到 State 这个枚举类中可以发现这里指的状态
// 其实就只有两种,处于 STARTED 或者 RESUMED 才认为当前的观察者是 active
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

再来看activeStateChanged(), 这个方法在抽象类 ObserverWrapper

void activeStateChanged(boolean newActive) {
// 判断当前状态是否改变,没有改变就不执行后面的逻辑
// mActive 初始化状态为false,因此刚订阅的时候会跳过这个判断
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
// 上面的英文注释为原代码中的注释,很好理解,这里就是先立马同步状态
mActive = newActive;
// 判断当前 LiveData 是否为未激活状态,这里不理解没关系,继续往后看
boolean wasInactive = LiveData.this.mActiveCount == 0;
// 如果当前这个观察者的状态为 active,就给 LiveData 的 mActiveCount 加一,否则就减一
LiveData.this.mActiveCount += mActive ? 1 : -1;
// 如果 LiveData 之前的状态是未激活,而当前这个观察者的状态为激活,
// 说明LiveData的状态马上要变成激活状态了, 回调 onActive 函数
if (wasInactive && mActive) {
onActive();
}
// 和上面的逻辑相反,同理当 LiveData 所有的观察者都进入为激活状态后,
// 这个 LiveData 的状态也进入未激活,回调 onInactive 函数
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
// 上面的代码都是为了回调一些状态函数,可以类比为 LiveData 的生命周期函数
// 下面才进入到和这个观察者切身相关的代码
if (mActive) {
// 从函数名字可以看出这里是在分发数据,并且传入了this即这个ObserverWrapper
dispatchingValue(this);
}
}

上面的代码我们可以知道一个 LiveData 很关键的点,当回调 onInactive 或者 Livedata 的 mActiveCount 为 0 的时候,并不是说 LiveData 没有观察者,而是它所有的观察者当前都处于未激活状态,这里千万不要搞混淆了。

另外可以看到这里可能会触发一次value 的分发,这里就解释了开篇第三个问题的一半,当我们第一次订阅LiveData的时候,会去绑定生命周期,然后会回调到 onStateChanged,当处于active后就会触发一次value的分发

三、当 setValue的时候发生了什么

第二个部分我们已经可以得出分发value是通过dispatchingValue();方法来实现的,那setValuepostValue 应该最终都会调用这个函数,我们先看 setValue

@MainThread
protected void setValue(T value) {
// 该方法只能在主线程调用
assertMainThread("setValue");
// 更新版本号
mVersion++;
// 更新 mData 的值
mData = value;
// 分发数据
dispatchingValue(null);
}

上面的代码很简单,所以核心在 dispatchingValue(null); 中,大家细心一点可以发现这里传入的参数为null ,而上面ObserverWrapper 传入的参数为 this:

// 参数为 ObserverWraper
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// 先看后面,最后再返回来看这里
if (mDispatchingValue) {
// 如果当前正在分发,就标记当前分发失效
mDispatchInvalidated = true;
return;
}
// 标记当前正在分发数据
mDispatchingValue = true;
// do..while 这个循环至少会执行一次
do {
// 标记为正常分发状态
mDispatchInvalidated = false;
if (initiator != null) {
// 关键点:如果传入的参数不为空
// 字面理解:考虑通知这个观察者
considerNotify(initiator);
initiator = null;
} else {
// 如果传入的参数为空,遍历之前我们订阅时存储观察者的那个map
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//考虑通知这个观察者
considerNotify(iterator.next().getValue());
// 如果分发状态被标记为失效了,跳出这次遍历通知的逻辑
if (mDispatchInvalidated) {
break;
}
}
}
// 当分发状态标记为失效时 循环do中的逻辑
} while (mDispatchInvalidated);
// 标记分发完成
mDispatchingValue = false;
}

上面的代码主要有三个点

  • 当传入了 wrapper 就会只通知这个观察者,也就解释了开头第三个问题
  • 这个分发考虑一个多次分发value的问题,而处理的方式是停止前一次的分发,立即进入下一次的分发。比如第一次分发开始,紧接着更新了value,调用 dispatchingValue,这时候会发现当前正在分发,于是将mDispatchInvalidated 标记为 true, 第一次分发在for循环中发现本次分发失效了,跳出for循环,然后while循环判断通过将再一次执行do中的代码,这时候会将mDispatchInvalidated标记为false,顺利的走完for循环中的逻辑,跳出while循环,最后标记分发完成。
  • 无论是通知一个还是通知全部,都进入到了considerNotify(wrapper); 这个函数中
private void considerNotify(ObserverWrapper observer) {
// 这里面的代码就很好理解了,保留了原代码中的英文注释
if (!observer.mActive) {
// 如果当前observer 的状态为未激活 CREATED, STOPED, PAUSED
// 注意 DESTORYED 不在未激活状态中,会直接移除 observer
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
// 英文注释解释得很清楚了,有可能宿主进入了未激活状态,但是并没有回调 onStateChanged
// 或者因为某些原因导致没有收到event,所以这里需要手动调用一次来进行判断
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
// 对比当前分发数据的版本号和该观察者当前持有的版本号
if (observer.mLastVersion >= mVersion) {
return;
}
// 更新观察者的版本号
observer.mLastVersion = mVersion;
// 回调给观察者 value
observer.mObserver.onChanged((T) mData);
}

上面的代码就解释了开头的第一个问题

四、扩展问题

postValue 是如何实现的?

从源码可以看出来,当多次调用postValue时,如果主线程还没来得及处理前面的更新,则只会更新最后一次的value,并不会 post 多次runable到主线程去执行

protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
// 当 mPendingData 为 NOT_SET 标记 postTask 为true
postTask = mPendingData == NOT_SET;
// 将 mPendingData 的值更新为 value
mPendingData = value;
}
// 如果 postTask 为false, 即在postValue 之前mPendingData 的值不是NOT_SET
// 说明上一次postValue的任务还没有执行完,不需要重复提交
if (!postTask) {
return;
}
// post 一个runnable到主线程执行,深入到 postToMainThread 方法可以知道就是通过一个
// 绑定了主线程looper的handler来实现的
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
// 将 mPendingData 重置为 NOT_SET 来标记本次任务完成
mPendingData = NOT_SET;
}
// setValue
setValue((T) newValue);
}
};

MutableLiveData 和 LiveData 的区别

这个甚至不用看源码,熟悉kotlin就知道跟 List 和 MutableList 的区别一样。LiveData 中 setValuepostValue 都是 protected, 而MutableLiveData 把它们变成了 public

推荐 MutableLiveData 只提供给有修改data权限的类使用,对于一些只取值的类(UI等)则提供 LiveData,避免被错误修改。

Transformations.map 和 switchMap

如果之前对 RxJava 比较熟悉的同学基本都能理解这两个方法的区别和原理。本质上和 RxJava 的 map 和 flatmap类似

public static <X, Y> LiveData<Y> map(
@NonNull LiveData<X> source,
@NonNull final Function<X, Y> mapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
// 利用MediatorLiveData 的特性订阅原LiveData
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
// 然后将原LiveData 通过传进来的mapFunction转换后回调给观察者
result.setValue(mapFunction.apply(x));
}
});
return result;
}
public static <X, Y> LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
// 和map一样,核心也是传进来的这个转换函数
// 不同的是 map的转换函数是将原livedata的值处理/转换另外一个类型的值(当然也可以同类型)
// 而这里的转换函数是将原Livedata 的值处理后包装到另一个Livedata中并返回
LiveData<Y> newLiveData = switchMapFunction.apply(x);
if (mSource == newLiveData) {
return;
}
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}

事实上我们完全可以自己去根据项目需求来自定义一些类似RxJava的操作符。

五、总结

没有总结,以后关于源码的文章都不写总结了。希望自己和看到这篇文章的人都能静下来认真看完源码学习的过程,然后得出自己的理解和总结,而不是看了两段就直接跳到后面来背总结。

支持一下
如果对你有帮助,也可以支持下小站