Android-Jetpack LiveData

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

前言

livedata 作为Jetpack 的重要组件,很多细节需要注意,这里也是记录一下自己之前不理解的地方,好记性不如烂笔头

如何理解 LiveData 具有生命周期感知能力

当界面处于可见(激活状态)时候才能收到数据的变化

举个简单的例子,两个界面,MainAct 和 SecondAct 共同处理一个 LiveData

界面如下

MainActSecondAct

通过打印能够看到

1
2
3
4
5
6
7
8
9
10
11
2020-09-17 20:12:35.877 I: main Act data 1
2020-09-17 20:12:36.828 I: main Act data 2
2020-09-17 20:12:37.505 I: main Act data 3
2020-09-17 20:12:38.920 I: Main Act 跳转 Second Act
2020-09-17 20:12:38.993 I: second Act data 3
2020-09-17 20:12:44.384 I: second Act data 4
2020-09-17 20:12:44.977 I: second Act data 5
2020-09-17 20:12:45.360 I: second Act data 6
2020-09-17 20:12:45.570 I: second Act data 7
2020-09-17 20:12:47.864 I: second Act finish
2020-09-17 20:12:47.948 I: main Act data 7
  1. 当我们点击到3的时候 跳转到 SecondAct 界面 立马触发了 livedata 将 在SecondAct 的状态改成3
  2. 在SecondAct 的时候 点击+1 只有SecondAct 界面发生变化,MainAct 不会发生变化
  3. 当回退到MainAct 的时候 立马触发了MainAct 的 livedata 变化 并且只会显示最新的状态

liveData 是如何工作的

基于上面的小示例,我们能够大概猜测到,MainAct 和 SecondAct 两个界面都注册了观察者,被livedata 保存了起来,当点击+1 的时候,在遍历所有的观察者,通知其状态发生了变化,

首先 livedata 有一个观察者,观察状态变化的,内部呢,也有一个观察者,通过lifecycle 观察界面的生命周期变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//判断是否是主线程
assertMainThread("observe");
// 如果当前的 lifecycle 已经GG了 就不管了
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//创建一个 lifecycle 的观察者
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//保存在 mObservers map 当中
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);
}

当我们+1 的时候

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
@MainThread
protected void setValue(T value) {
//省略代码
dispatchingValue(null);
}

void dispatchingValue(@Nullable ObserverWrapper initiator) {
//省略代码
do {
//省略代码
if (initiator != null) {
considerNotify(initiator);
} else {
//遍历所有的状态观察者
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//通知改变
considerNotify(iterator.next().getValue());
}
}
} while (mDispatchInvalidated);
}


private void considerNotify(ObserverWrapper observer) {
//省略代码
//通过接口记录状态变化
observer.mObserver.onChanged((T) mData);
}

就这样我们完成了一次状态的变化

livedata 如何处理未激活的观察者不接受变化的数据

还是上面的小例子, 在SecondAct 的时候 点击+1 只有SecondAct 界面发生变化,MainAct 不会发生变化

查看源码分析

在我们调用 observe方法的时候 创建了 LifecycleBoundObserver

1
2
3
4
5
6
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//创建了一个 LifecycleBoundObserver
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
}

查看 LifecycleBoundObserver

1
2
3
4
5
6
7
8
9
10
11
12
13

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
//这里是 监听界面变化的 观察者
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
//当界面生命周期发生变化的时候 调用下面的方法
activeStateChanged(shouldBeActive());
}
}

看到这里 你就能够猜测到,在 MainAct 界面创建的时候,会触发 3次 activeStateChanged 方法,这3次触发其实啥事没干,主要就将观察者中的 mActive 属性

1
2
3
private abstract class ObserverWrapper {
boolean mActive;
}

简易版的流程图

在onStart 的时候 将 mActive 属性置为 true ,

跳转到 SecondAct 的时候,MainAct 执行了 onStop 方法 也是在这里 将 mActive 改成 false

当我们点击+1 的时候, livedata 会计数累加

1
2
3
4
5
6
7
8
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
//version 累加
mVersion++;
mData = value;
dispatchingValue(null);
}

然后准备通知所有的观察者 数据变化了

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

private void considerNotify(ObserverWrapper observer) {
//这里发现 MainAct 的 状态是不可用的 GG 不给他发事件
if (!observer.mActive) {
return;
}
//检查最新状态b4调度。也许它改变了状态,但是我们还没有得到这个事件。
//我们仍然首先检查observer.active,以将其保留为事件的入口。因此,即使观察者
//进入活动状态,如果我们尚未收到该事件,也最好不要通知以获得更可预测的通知顺序



if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}

//刚刚点击了一下 计数+1 所以 mLastVersion(-1)< mVersion (0)
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//通知状态变化了
observer.mObserver.onChanged((T) mData);
}

但这里就能够看到 为啥 在SecondAct 的时候 点击+1 只有SecondAct 界面发生变化,MainAct 不会发生变化

当界面激活的时候,livedata 如何将最新的数据发给观察者

在上面的例子中 当回退到MainAct 的时候 立马触发了MainAct 的 livedata 变化 并且只会显示最新的状态

当MainAct onStart 的时候界面,界面被激活 ,触发 onStateChanged 方法,而且 observer.mLastVersion < mVersion,所以会将最新的一次数据变化发送给MainAct

livedata 作为事件发送的问题

如果都是在主线程上发送,肯定没啥问题

看一下代码

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

事件不会少,但是如果在子线程上发送呢

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

protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}


private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};

想一下 如果实在多线程上处理,mPostValueRunnable 很有可能会重置掉,导致丢失事件

onActive 和 onInactive

这两个方法 在 livedata 内部是没用的,主要是给自定义 livedata 时候使用的

触发的条件

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
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
//当第一个观察者进入以后 这个mActiveCount == 0 wasInactive = true
boolean wasInactive = LiveData.this.mActiveCount == 0;

//当界面被激活 就计数+1 如果界面状态被置成未激活 就计数-1
LiveData.this.mActiveCount += mActive ? 1 : -1;

//只有第一个界面的时候 wasInactive 才是 true 当Second 进入的时候 因为计数+1了 所以 变成了2 不在满足 ==0 的条件
if (wasInactive && mActive) {
//也是在第一个界面onStart 的时候才会触发
onActive();
}

//在上面我们能够看到 如果界面 置成未激活 就计数-1 当MainAct 也 onStop 以后,那么 mActiveCount 就==0
if (LiveData.this.mActiveCount == 0 && !mActive) {
//执行下面的方法
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}

observeForever

这个方法和 observe 最大的区别在源码中可以看到

observe。创建的是一个 LifecycleBoundObserver ,他的判断条件根据lifecycle 判断

1
2
3
4
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

当界面onDestory 的时候

1
2
3
4
5
6
7
8
9
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
//remove了
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}

observeForever 创建的是一个 AlwaysActiveObserver,则直接返回为true

1
2
3
4
@Override
boolean shouldBeActive() {
return true;
}

因为不在具有生命感知能力,就需要手动去自己remove,并且 即使mainAct 界面处于未被激活的状态 也依然可以收到

那么他是如何处理即使在未被激活的界面 依然能够触发变化呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
//很粗暴,直接将界面状态变成激活状态,不高那么多花里胡哨的
wrapper.activeStateChanged(true);
}

参考

官网

Android LiveData 使用详解