前言

ViewModel + LiveData 是很推荐和不错的写法, LiveData 是感知生命周期的, 所以对于界面来说, 观察数据变化及接收。 其实说白了就是观察者模式, 并且加上了生命周期限制, 在一定的生命周期中可以回调出去, 其他生命周期不再回调而已。 到底是怎么实现的呢, 我们可以着重分析下。

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MainModel : ViewModel() {
val strResult = MutableLiveData<String>()

fun requestStr() {
// ....
strResult.value = "v1"
// ....
strResult.postValue("v2")
// ....
}
}

mModel.strResult.observe(this) { strResult->
}

源码分析

基本使用就是这样子拉。 LiveData 的 setValue 是必须在主线程设置数据, 而 postValue 是不限制在什么线程中设置数据, 会自动切换到主线程回调数据。 我们先看看设置数据是怎么处理的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MutableLiveData<T> extends LiveData<T> {

public MutableLiveData(T value) {
super(value);
}

public MutableLiveData() {
super();
}

@Override
public void postValue(T value) {
super.postValue(value);
}

@Override
public void setValue(T value) {
super.setValue(value);
}
}

我们可以很清晰的看到, setValue 和 postValue 调用了父类的 LiveData 的实现。
我们先看看 setValue。

1
2
3
4
5
6
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}

是不是很简单, 检验下是不是主线程, 然后把数据设置到 mData 成员变量上, 然后直接调用分发。 分发的话, 我们稍后一起分析。
我们看下 postValue。

1
2
3
4
5
6
7
8
9
10
11
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

因为 postValue 可能是在分线程中调用的, 所以在设置数据的时候加了一把锁。 把数据设置到了 mPendingData 成员变量上。 我们可以发现 postTask 变量为 false 的时候可能会直接 return, 侧面我们也要认识到一个问题, 就是 postValue 是会丢数据的。 丢数据的条件就是 mPendingData 是不是等于 NOT_SET, 也就是是不是上一条 postValue 是否处理完了, 如果没有处理完的话, 就不再设置了。 坑不坑你说, 所以使用的时候一定要谨慎谨慎再谨慎。 设置完数据后, 直接调用了 postToMainThread 回调到主线程去运行, 我们直接看 mPostValueRunnable 的实现吧。

1
2
3
4
5
6
7
8
9
10
11
12
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};

很简单对吧, 把数据又再次调用 setValue 去了。 我们在这里也观察到 mPendingData 设置了 NOT_SET, 和 postValue 中对于起来了。 如果当前 runnable 还没有处理到这里的话, 那么 mPendingData 就有数值, 那就再来数据就抛弃了。
OK, postValue 还是最后调用了 setValue, 所以我们最后看看怎么分发的吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}

对于 mDispatchingValue 这些变量就不再解释了。 我们看下关键的代码, 不管传入的 ObserverWrapper 参数是否为空, 都是会调用 considerNotify 方法。 在 setValue 会分发的时候明确是传入的 null 的, 所以我们需要关注下 mObservers 变量, 是观察者模式哈。 我们先看看是怎么分发通知的吧。

1
2
3
4
private void considerNotify(ObserverWrapper observer) {
// ......
observer.mObserver.onChanged((T) mData);
}

我们直接看最后一行吧, onChanged 就回调出去了。 我们现在看看我们是怎么往 mObservers 添加我们的观察者的。

1
2
mModel.strResult.observe(this) {
}

肯定是这个没跑了。 我们看看 observe 怎么处理的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
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);
}

很明显当前线程必须是在主线程中调用的, 其二呢, 如果当前界面或者组件生命周期已经走到尽头了, 那么就不再观察了哈。
我们可以看下下面几个主要的方法。 把 LifecycleOwner 和 Observer 传入构造了一个新的 LifecycleBoundObserver。 从名称也可以看到很明显内部肯定基础了 LifecycleObserver 去关注了生命周期, 从最后一行也能正面因为是 addObserver 了。
我们也终于看到 mObservers 去添加了。 我们现在看看 LifecycleBoundObserver 干了啥。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}

void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}

看看这两个方法的实现, 其中 shouldBeActive 是判断当前是否在合理的生命周期, 我们可以看到如果在 STARTED 生命周期后面都可以。
我们看看生命周期发生改变的回调里都做了什么。

  • 如果生命周期到了尽头, 就移出当前监听。
  • 如果生命周期不一致的话, 就会循环去调用 activeStateChanged
  • 我们可以看到在 mActive 为 true 的时候会再次分发当前数据。

通过第 3 点我们可以了解到, 当生命周期第一次进来的时候隔离生命周期的时候, 就会分发一次。 所以在设置回调时会进行分发一次。 如果界面没有走到尽头, 也就是没有运行到 onDestroy 这一步的话, 如果状态再次又 STARTED 之前进来, 且 observer.mLastVersion < mVersion, 那么还会再次回调分发一次。
至于再次回调可能会造成的问题, 我会在最后单独说。
了解到这里的事情, 我们再次看下分发通知的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
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.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}

前面的 mActive 和 shouldBeActive 又再次的判断了生命周期是否在合理的情况。 只有再合理的情况下的话才会分发。 observer.mLastVersion >= mVersion 这个判断呢, 我们可以看下 mVersion 只有在 setValue 中才会递增, so 也保证了只有改数据没有分发过的话才会再次分发。

再次回调时的问题(数据回灌)

先举一个例子, 我们在请求网络然后通过 LiveDate 从 ViewModel 中订阅数据回调, 然后通过结果进行逻辑处理。 如果我们已经请求过后处理完毕了。 如果这个时候去修改了系统文字大小, 然后再次进入界面时, 网络请求的那个数据会再次回调过来, 那么再处理逻辑, 可能会出现问题。
在 Activity 由内存不足或者配置发生改变了(比如修改了系统文字大小再次进入), 界面会再次还原重建, 这个时候 ViewModel 还是原来的那个呢。 所以再次进入的时候, 会把之前设置的值再次回到到当前界面。 所以我们需要非常谨慎谨慎再谨慎。 在使用 LiveData 时已经要考虑到该情况。