本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。
一、概述
LiveData作为2017Google I/O大会作为架构组件的一部分被共同发布出来。LiveData是具有生命周期感知,可观察数据的容器。能够确保只有在组件处于活跃状态时才会更新数据。它不仅可以和ViewModel组合使用,同时也可以单独使用。LiveData基于观察者模式设计,并且只会更新处于活跃状态的观察者(START 及RESUME),而处于非活跃状态的观察者不会收到通知。
二、简介和基础使用
2.1LiveData的简介
使用 LiveData 具有以下优势:
确保界面符合数据状态
LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。观察者可以在每次发生更改时更新界面,而不是在每次应用数据发生更改时更新界面。
不会发生内存泄漏
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
共享资源
您可以使用单一实例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。如需了解详情,请参阅扩展 LiveData。
2.2LiveData用法
2.2.1 基本用法
//MainActivity.kt
mainViewModel.navigateToDetailAction.observe(viewLifecycleOwner, EventObserver { //1
pokemon ->
//跳转到另一个页面
findNavController().navigate(toPokemonDetail(pokemon),
FragmentNavigatorExtras(cardView to pokemon.url))
})
.....省略.....
//MainViewModel.kt
private val navigateToDetailAction = MutableLiveData<Pokemon>()
fun openDetail(pokemon: Pokemon) {
navigateToDetailAction.value = pokemon //2
}
复制代码
注释1处的observe方法有两个参数分别是LifecycleOwner和 Observer ,第一个参数就是MainActivity本身,第二个参数新建了一个Observer,在onChanged方法中得到回调。注释处的postValue方法会在主线程中更新数据,这样就会得到打印的结果。
三、原理
3.1 LiveData类图
可以看到LiveData类十分简单。
首先LiveData本身是一个抽象类,但是其并没有抽象方法。它有个LifecycleBoundObserver内部类,实现了LifecyclerEventObserver接口,这样当生命周期有变化时,就会通过onStateChanged方法回调告知。并继承ObserverWrapper抽象类,用于处理观察者的配置。
MutableLiveData为LiveData的实现类,其内部只是将setValue及postValue方法设置为public.用于说明其内部值是可变的。
MediatorLiveData可以看成是多个LiveData的代理,当将多个LiveData添加到MediatorLiveData,任何一个LiveData数据发生变化时,MediatorLiveData都会收到通知。
3.2源码解析
在源码解析前,将带着以下三个问题进行源码的阅读
1.LiveData是如何知道生命周期变化的?
2.LiveData调用observe方法后是如何回调通知的?
3.LiveData的postValue/setValue方法的流程?
我们把最常用到的observe方法作为源码解析的入口
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//1.只能在主线程添加监听
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//2.生成了绑定了Lifecycle生命周期的Observer
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//3.将观察者添加到Map中。putIfAbsent方法为如果当前key已经存在,则返回对应的value。
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;
}
//4.使得LiveData获得生命周期感知的能力
owner.getLifecycle().addObserver(wrapper);
}
}
复制代码
在调用LiveData.observer()方法时,传递的第一个为 LifecycleOwner,第二个参数Obserser实际就是我们的观察后的回调。
第二步将两个参数传入了包装类,在包装类中实现了Lifecycle生命周期的监听及obersrver的操作。
第三步将观察者及上面的包装类存储到mObservers中。如果在Map中已经存在则返回对应的值。
第四步:添加到Lifecycle的观察者队列中。使其能够感知到生命周期变化。
接着我们来分析LifecycleBoundObserver这个包装类
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
....省略代码
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
//是否处于活跃状态
@Override
boolean shouldBeActive() {
//此代表了在Acitivty中,处于onStart,onResume,onPause生命周期时,LiveData才会处于活跃状态。
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//1.在Lifecycle处于销毁状态时,移除了观察者。这样就阻止了内存泄漏的发生
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
//2.更新LiveData的活跃状态
activeStateChanged(shouldBeActive());
}
....省略代码
LiveData#ObserverWrapper.class
void activeStateChanged(boolean newActive) {
//如果状态没有变化,则直接返回
if (newActive == mActive) {
return;
}
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
//处于活跃状态的observer个数从0到1
if (wasInactive && mActive) {
//默认空实现
onActive();
}
//处于活跃状态的observer个数从1到0
if (LiveData.this.mActiveCount == 0 && !mActive) {
//默认空实现
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
复制代码
dispatchingValue的具体实现
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
//正在处于分发状态
if (mDispatchingValue) {
//分发无效
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
//在setValue方法中会调用此方法,此时initiator为null,则会通知所有处于活跃状态的观察者。否则只会通知initiator自己。
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;
}
@SuppressWarnings("unchecked")
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
//首先判断是否处于活跃状态,避免在非活跃状态时收到回调
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//version判断。如果观察者的version大于当前version则不通知更新.
//因此LiveData并不会根据具体值是否改变而去判断是否需要通知观察者
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 调用onChanged方法告知
observer.mObserver.onChanged((T) mData);
}
复制代码
LiveData的postValue/setValue方法分析
首先,修改LiveData的值有两个方法,一个是setValue(),一个是postValue()。从名字上就可以知道两个方法的区别。postValue方法是可以在子线程中调用的,最终通过Handler调用setValue方法。因此下面直接分析setValue方法的流程
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
...省略
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
// 最终会在主线程调用setValue方法
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
//增加version的值
mVersion++;
mData = value;
//在setValue调用完之后,通知其它的观察者更新
dispatchingValue(null);
}
复制代码
四、常见疑问
1.在Activity的任何生命周期下修改Livedata值,都会收到回调吗?
答:不会,使用observer方法监听时,只有处于STARTED状态的Lifecycle才能收到回调
2.什么情况下LiveData处于Active状态?
答:是由ObserverWrapper(私有类)中的shouldBeActive方法的实现决定。在使用observe()方法监听时,默认实现类为LifecycleBoundObserver
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
复制代码
3.LiveData为什么不会内存泄漏?
答:如前面分析所述
4.LiveData特性导致的问题。–网上常说的黏性事件
答:
//MainViewModel.kt
private val navigateToDetailAction = MutableLiveData<Pokemon>()
fun openDetail(pokemon: Pokemon) {
navigateToDetailAction.value = pokemon
}
//MainActivity.kt
mainViewModel.navigateToDetailAction.observe(viewLifecycleOwner, Observer {
pokemon ->
//跳转到另一个页面
findNavController().navigate(toPokemonDetail(pokemon),
FragmentNavigatorExtras(cardView to pokemon.url))
})
复制代码
调用了openDetail方法后,改变了LiveData中的值,就会通知到MainActivity中的观察者。然后拉起了一个界面,此时如果点返回键,会发现马上又跳回该页面,造成无法返回的现象。
这是由于当前界面跳转到另一个页面时,active由活跃变为了不活跃,而返回到该页面时,由不活跃变为了活跃状态,(触发了LifecycleBoundObserver 的onStateChanged()方法)因此此时回调会被调用,从而再次跳转到了第二个页面。
解决方法:1.在此种场景下,让LiveData只调用一次。调用一次后不再通知。
5.如果想忽略生命周期变化一直监听要如何实现?
答:使用observeForever添加监听,使用此方法添加监听后,注意:在销毁时需要手动手动调用removeObserver()移除监听,否则可能会造成内存泄漏
6.使用相同的值去调用setValue及postValue方法时,是否会收到回调?
答:会,详细看上面的源码分析。与version相关,跟具体值无关。
7.为什么LiveData会带有粘性特性?
假设在activity1中,有定义LiveData变量a,此时你并没有在该页面添加观察者,而是在activity2的onCreate中添加观察者。
当你在activity1,setValue或者postValue后,跳转到activity2,你会发现activity2中的观察者的回调起效了。仔细看源码,你会发现因为当你打开activity2,并且添加了观察者时,符合了生命周期变动 并且由于 observer.mLastVersion < mVersion ,因此调用了observer的onChanged方法。
8.为什么会在添加监听后会收到多次回调?
答:排查思路:
1.查看对应LiveData 监听者的数量。是否在无意中多次调用了observe方法。比如:在onResume中调用observe方法。
2.查看在添加监听前,是否使用了postValue更新数据。
五、总结
本篇主要介绍了LiveData的用法及原理分析。总的来说,LiveData是基于Lifecycle实现了数据分发能力。