Android Architecture Components分析记录(二)

记录分析AAC第二篇—LiveData,官方地址
https://developer.android.com/topic/libraries/architecture/livedata.html?hl=zh-cn

LiveData

LiveData是一个数据持有类并赋予数据Observer属性,使用LiveData能够在有观察者的时候触发获取请求,并在生命周期符合OnStart状态条件下通知观察者数据变化,所以官方很吊的说明下面几点

  • No memory leaks
  • No crashes due to stopped activities
  • Always up to date data
  • Proper configuration change
  • Sharing Resources
  • No more manual lifecycle handling

使用

比如最常用的UserInfo

1
2
3
4
5
6
7
8
9
10
public class UserInfoLiveData extends LiveData<UserInfo> {
//当有观察者观察时会触发onActive
@Override
protected void onActive() {
super.onActive();
//假设获取UserInfo并返回
UserInfo userInfo = getUserInfo();
setValue(userInfo);
}
}

Activity添加观察代码

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UserInfoLiveData userInfoLiveData = new UserInfoLiveData();
userInfoLiveData.observe(this, new Observer<UserInfo> {
@Override
public void onChanged(@Nullable UserInfo userInfo) {
//此方法会在LiveData的setValue被调用
//在这里更新UI
}
});
}

分析

进入正题,这次从LiveDataobserve方法开始追寻,看看发生了什么

observe(LifecycleOwner, Observer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
new SafeIterableMap<>();
@MainThread
public void observe(LifecycleOwner owner, Observer<T> observer) {
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && existing.owner != wrapper.owner) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
wrapper.activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
}

这个observe方法源码上面一堆密密麻麻的注释,大概重点就是当数据有变化的时候就会通知观察者。
首先不用多说,确定下当前生命周期的状态,不满足条件就return,之后将传进来的ownerobserver``new了个LifecycleBoundObserver,先不管。然后通过mObserversMap中看看有没有相同的LifecycleBoundObserver,根据Observerkey去查找,如果存在也retuen。这里注意官方不允许同个Observer添加到不同的owner。接着调用owner.getLifecycle().addObserver(wrapper);。。。看来这个LifecycleBoundObserver也是实现了LifecycleObserver(至于这个点可以看第一篇)。最后调用LifecycleBoundObserveractiveStateChanged方法,具体实现刚好跟刚才不管的一起分析

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
34
35
36
37
38
39
40
41
42
class LifecycleBoundObserver implements LifecycleObserver {
public final LifecycleOwner owner;
public final Observer<T> observer;
public boolean active;
public int lastVersion = START_VERSION;
LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
this.owner = owner;
this.observer = observer;
}
@SuppressWarnings("unused")
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
void onStateChange() {
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(observer);
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
}
void activeStateChanged(boolean newActive) {
if (newActive == active) {
return;
}
active = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += active ? 1 : -1;
if (wasInactive && active) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !active) {
onInactive();
}
if (active) {
dispatchingValue(this);
}
}
}

刚才在observe方法内实例化时传进来的ownerobserver只是赋值了一下内部变量。其次owner.getLifecycle().addObserver(wrapper);意味着onStateChange()能够接收生命周期的变化通知,果不其然onStateChange中调用了
activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));

isActiveState() 返回 boolean -> 当前生命周期状态是否至少处于START状态之后

activeStateChanged方法会根据传进来的newActive状态去调用onActive()或者onInactive(),也就是当有观察者主动观察时会调用onActive()进行数据获取请求,并在请求数据成功后手动调用setValue(T)通知观察者数据变化。setValue(T)内调用dispatchingValue()方法最后回调onChanged(T)通知观察者数据变化从而更新UI。

一句句理解

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
//当有观察者观察的时候或者生命周期变化的时候会调用此方法
//newActive : 当前生命周期是否START状态之后
void activeStateChanged(boolean newActive) {
//如果新状态与当前状态一致则return
if (newActive == active) {
return;
}
//标明当前是否处于激活状态
active = newActive;
//mActiveCount是有多少个观察者在观察
//所以wasInactive表示在这之前的观察者数
boolean wasInactive = LiveData.this.mActiveCount == 0;
//相应的+-1
LiveData.this.mActiveCount += active ? 1 : -1;
//如果是第一个观察并且激活状态则回调onActive()去获取数据
if (wasInactive && active) {
onActive();
}
//对应的取消绑定
if (LiveData.this.mActiveCount == 0 && !active) {
onInactive();
}
//下发数据变化 会回调onChanged(T)
if (active) {
dispatchingValue(this);
}
}

dispatchingValue(LifecycleBoundObserver)

下面看dispatchingValue()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}

这个方法的逻辑判断主要依赖于两个boolean:mDispatchingValuemDispatchInvalidated。看完源码后感觉很巧妙,利用两个变量分发数据的变化通知观察者更新UI,并当有新的数据变化的时候break循环,减少了一次旧数据不必要的UI更新,很nice,点个赞。

mVersion

LiveData内部维护了一个变量mVersion数据版本控制,计算数据变化次数,并在dispatchingValue()下发中与观察者的内部计数version判断从而调用onChanged(T)

额外用法

LiveData还有两个很有用的API

  • observeForever(Observer observer)

    1
    2
    3
    4
    @MainThread
    public void observeForever(Observer<T> observer) {
    observe(ALWAYS_ON, observer);
    }

    ALWAYS_ON也是一个LifecycleOwner,但是永远处于RESUME状态下,也就是使用这个方法的观察者将永远接收到数据变化,无论生命周期的影响,所以在适当的时候需要开发者手动调用removeObserver(Observer)取消观察。

  • postValue(T value)

    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;
    }
    AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

    setValue(T)不同的postValue(T)允许在其它线程调用

Transformations of LiveData

官方提供Transformations工具帮助方便的转换LiveData并且依旧拥有被转换者的数据变化通知。下面给个🌰

map(LiveData source, final Function func)
switchMap(LiveData trigger, final Function> func)

1
2
3
4
5
6
7
8
9
10
11
12
LiveData<String> stringLiveData = Transformations.map(userInfoLiveData, new Function<UserInfo, String>() {
@Override
public String apply(UserInfo input) {
return input.getName();
}
});
stringLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
Log.i(TAG, "onChanged: s === " + s);
}
});

stringLiveData通过Transformations.map()方法实例化,在apply(UserInfo input)中取出需要的String数据返回,最后同样的observe一下,将会拥有的功能即:当userInfo数据改变,同样会通知此LiveData的观察者即回调onChanged(T)

分析

官方提供这个转换API的原因是开发者可能需要在数据变化发送给观察者之前对数据进行改变等操作,这样的确会方便很多。那么开始看源码吧~

1
2
3
4
5
6
7
8
9
10
11
@MainThread
public static <X, Y> LiveData<Y> map(LiveData<X> source, final Function<X, Y> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(func.apply(x));
}
});
return result;
}

先看map(),这里面涉及到三个Observer,可能会有点绕。首先一进来就实例了一个MediatorLiveData<Y>,也是一个LiveDatamap()返回的就是这个家伙。返回之前调用了addSource()传入了第一个Observermap()方法的第二个参数Function就是在这里被回调。接着看addSource()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
@MainThread
public <S> void addSource(LiveData<S> source, Observer<S> onChanged) {
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if (existing != null) {
return;
}
if (hasActiveObservers()) {
e.plug();
}
}

看起来似曾相识,LiveData.observe()的逻辑跟这个方法的逻辑差不多一个样,原理都是一样的。这里实例化了一个Source,主要是为了保存一下数据变化的version,好判断通知观察者的时机。
这里有个hasActiveObservers()的判断,判断是否有观察者观察,有的话执行plug(),那么看向Source

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
private static class Source<V> {
final LiveData<V> mLiveData;
final Observer<V> mObserver;
int mVersion = START_VERSION;
Source(LiveData<V> liveData, final Observer<V> observer) {
mLiveData = liveData;
mObserver = new Observer<V>() {
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
observer.onChanged(v);
}
}
};
}
void plug() {
mLiveData.observeForever(mObserver);
}
void unplug() {
mLiveData.removeObserver(mObserver);
}
}

plug()unplug()不必多说,标准的注册取消注册步骤。看看构造函数里面又来了个Observer,也就是第二个Observer,在回调函数onChanged里判断了下version,前后不一致的话手动调用observer.onChanged(v);,也就是第一个observer。有点绕了0 0

那么第三个Observer在哪里呢,其实就是一开始通过map()转换得来的LiveData:stringLiveData进行观察的observer。具体流程如下
1.通过map()转换拿到MediatorLiveData
2.调用observe()对转换来的MediatorLiveData进行观察
3.生命周期到达START后会自动调用onActive()
4.MediatorLiveData.onActive()会遍历调用plug()
5.plug()中对源LiveData调用observe()观察
6.源LiveData回调onChanged()Source中的observer(第二个observer)
7.继续回调第一个onChanged()也就是Transformations.map()中的Observer
8.result.setValue(func.apply(x));
9.最终回调第三个onChanged():开发者自己的observer从而更新UI

有点累,饶了半天,不过终于知道为何源数据发生数据变化时,新LiveData也能及时响应的原因。

至于switchMap()更粗暴更自由化,内部还会自动判断前后LiveData的不同自动取消观察等等,所以开发者不需要担心内存泄露的问题。

总结

LiveData的用法还是挺方便的,内部帮助持有需要的数据,并使用观察者模式对数据变化进行观察,并拥有生命周期的特效,所以不用担心内存泄露等问题。
对于像官方提供的例子中的Location或者开发项目中最常见的UserInfo,甚至可以用static修饰LiveData使其可以供应给所有需要的地方。