简介
google官网对LiveData的有一句很简短的介绍:底层数据库更改时,通知视图
LiveData具有数据传递的功能,它是一个数据传输的工具类,数据可以在它身上进行流动,有点像Handler,它可以发送数据,而且别人可以监听到它发数据这个动作。LiveData还可以绑定到数据库,只要修改数据源(数据库)UI界面也会跟着改变。LiveData不会发生内存泄漏,它会随着Activity的生命周期一起把observer给解绑。
注意:LiveData 本身不是数据,它能够作为一个观察者,观察到 Activity 和 Fragment 生命周期的变化,同时它又是一个被观察者,它能够被别人观察到它自己的数据发生了生命周期的变化,所以它自己是带生命周期的 。
LiveData很少单独使用,LiveData使用时外层一般都会包装一个ViewModel,ViewModel可以帮我们处理数据,比如屏幕旋转时数据的保存,下面通过一个实例看下LiveData的用法
LiveData实例
首先定义了一个 ViewModel
public class NameViewModel extends ViewModel {
private MutableLiveData<String> currentName;
public int i = 0;
public MutableLiveData<String> getCurrentName(){
if(currentName==null){
currentName=new MutableLiveData<>();
}
return currentName;
}
}在 Activity 中使用该 ViewModel
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
....
//这里把 i 变量放在 Activity里,它的值便会随Activity的生命周期变化
//这样当屏幕旋转时 它会归零,解决这个问题 只需要把该变量定义在 ViewModel里,也就是 NameViewModel
private int i=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
......
model= ViewModelProviders.of(this).get(NameViewModel.class);
//需要一个观察者来观察数据
Observer observer=new Observer<String>(){
@Override
public void onChanged(String s) {
nameTextView.setText(s);
}
};
//订阅
//LiveData 和 观察者绑定在一起了
model.getCurrentName().observe(this,observer);
btn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
String anotherName="jett"+(model.i++);
//LiveData 的set方法 必须在主线程,setValue 相当于发送了一个消息
//告诉订阅者数据发生改变了
//子线程调用 postValue
model.getCurrentName().setValue(anotherName);
}
});
}
}
源码分析
从绑定观察者开始入手
model.getCurrentName().observe(this,observer);LiveData.java
@MainThread //这里第一个参数是 Activity,第二个参数是 observer
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");//先判断是不是在主线程,不是的话会抛异常
//如果Activity的状态是 DESTROY 状态直接退出
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//把观察者和被观察者一起包装在一个类里面,观察者就是我们自己定义的那个匿名内部类,
//被观察者是 Activity
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//把包装类 以观察者自己为key存到一个集合中,这里是因为有可能会有多个观察者
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;
}
//同Lifecycles那一节添加观察者的动作是一样的,之后就会绕到原来的代码了
owner.getLifecycle().addObserver(wrapper);
}绑定好了之后,来看一下发消息(调用setValue)怎么发的
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");//先判断是不是在主线程
mVersion++;//LiveData里的一个变量,每一次setValue该变量都被加1
mData = value;//这里的value我们传了一个字符串
dispatchingValue(null);//分发
}在看dispatchingValue源码之前,我们先来看一下LiveData 及其观察者中的两个重要变量 mVersion 、mLastVersion,在后续分析中将会用到
public abstract class LiveData<T> {
static final int START_VERSION = -1;
private int mVersion = START_VERSION;//LiveData中的一个重要变量
private abstract class ObserverWrapper {//LiveData的内部类
final Observer<? super T> mObserver;
boolean mActive;
//observer中的 mLastVersion 的初始值也是 -1,
//如果有多个观察者大家都等于 -1
int mLastVersion = START_VERSION;//初始值是 -1接下来我们进入 dispatchingValue
//该方法第一次进来是传的空null
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {//第一次传参为null,会进入else
//获取之前的集合的迭代器,把每一个观察者取出来
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//getValue方法返回一个观察者,拿出来后发一个通知
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}//被观察者(Activity) setValue 的时候要发通知
private void considerNotify(ObserverWrapper observer) {
//该方法会先做一个状态判断,之后做version判断
//判断是不是活动状态,不是直接退出,这个活动状态表示的就是RESUMED
//Activity RESUMED 之后屏幕才会显示出来
if (!observer.mActive) {
return;//不是在活动状态证明你的界面是在后台,在后台的话消息就不会发给你
}
//如果是在前台会再次做一个判断
//shouldActive 会判断当前Activity的当前状态是不是 RESUMED
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//当我们第一次setValue执行到这里时 mVersion 在setValue方法中被+1后变为0
//mLastVersion 的仍然为 -1,所以这里不会退出
if (observer.mLastVersion >= mVersion) {
return;
}
//注意:上面的三个if 判断只要不通过就会一直在这跑
//它不会造成死循环,因为它肯定可以找到一个在前台的 Activity,代码是一个类似于死循环的代码
// 这里将LiveData的mVersion 赋值给观察者的 mLastVersion 观察者的 mLastVersion 也变为 0
observer.mLastVersion = mVersion;
//上面三个 if 就是为了判断你的数据是不是最后一次更新的,这样就可以保证你的最新的数据能够
//更新到界面显示,当Activity没有在前台时是不会执行的,提高了效率
//这个observer 的 onChanged方法是我们自己在NameActivity中实现了,可以用最新的数据来更新UI
observer.mObserver.onChanged((T) mData);
}我们进入 activeStateChanged 方法
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;//检查有多少个是显示在前台
if (wasInactive && mActive) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this);//再次迭代执行分发,这次将执行initiator != null的情况
}
}注意:initiator != null的情况 会进入considerNotify(initiator);
所以 dispatchingValue 和 considerNotify 这两个方法在来回地一直跑,一直执行
接下来我们看一下在子线程中调用的postValue方法
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}//前面的代码都是同步处理
//核心代码:子线程取用的时候还是回归到主线程里面
//用一个线程池,让线程切换到主线程来执行
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}public void postToMainThread(Runnable runnable) {
//参数 runnable就是 mPostValueRunnable ,在它的run方法里执行了 setValue
mDelegate.postToMainThread(runnable);
}看一下 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);//当在其他线程里执行时,最终还是会调用到主线程执行setValue
}
};LiveData的用法
LiveData 的一个总线用法
在实际的开发中有很多 UI ,会有很多 LiveData跟它们统一再用,我们可以把多个这种 LiveData(它们全部放在内存里会很消耗内存)
放在一个总线里,做一个总线 LiveDataBus,简单说就是一个集合,如果说 Activity 要用的时候,都可以把自己要用的 LiveData放到
这个总线 LiveDataBus里,总线整个项目里有一个就够了
LiveData 正常的用法流程: 1. 先 new 出一个 LiveData---->2. 把 observer 绑定到该 LiveData----->3、执行 setValue (setValue其实是去执行 onChanged 方法)
当我们使用总线时 LiveData的用法流程:(这里有个问题:先发消息后绑定) 1. 先 new 出一个 LiveData---->2. 执行 setValue (setValue其实是去执行 onChanged 方法)---->3.把 observer 绑定到该 LiveData(UI 有了) ------>执行下一个 setValue...... 所以上面问题的解决方案:我们只需要消费掉 第二步的 setValue 让它无效,之后UI已经显示出来了,再setValue就回归到正常流程里了,不会出问题了 失效的方法:上面分析源码的时候,considerNotify 遇到的那三个 if ,随便处理哪一个都可以,不过前两个是用来保活的,尽量不要修改,可以改变第三个 if 判断 我们可以让第三个 if 满足条件:这里可以修改 mLastVersion 变量,这样第一次操作就会直接返回 修改 mLastVersion 变量 可以使用 hook 技术勾到源码的运行层面上来
时序图
最后来一张时序图,整理一下思路。(注:addObserver的流程时序图已经在 Jetpack之Lifecycles 那篇文章中详细画过了,这里就省略了)