概论
LiveData 是基于观察者的消息订阅/分发组件,能感知宿主(Activity/Fragment)的生命周期,这种感知能力可确保 LiveData 仅将消息分发给与活跃状态的观察者,即只有处于活跃状态的观察者才能收到消息,同时,LiveData的跨线程通信的能力也是其一大特点。
基于生命周期,其实就是在当前宿主 LifecycleOnwer 注册一个 Observer,那么宿主每次生命周期的变化,都会回调给观察者的 onStateChange() 方法,即便是刚刚注册的观察者宿主也会回调 onStateChange() 方法,会有一个状态同步的过程,LiveData 也是利用这个能力,巧妙实现了当宿主销毁的时候,自动移除注册进来的 Observer,从而避免了手动移除的麻烦。更不会造成内存泄漏,这个也是它的核心思想。
理解LiveData的原理,我们先从使用过程开始谈起。LiveData是一个abstract的抽象类,我们通常使用它的子类 MutableLiveData 来实现通信。
public abstract class LiveData<T> {
......
}
MutableLiveData则很简单,只是简单的继承了 LiveData, 同时修改了 LiveData 中发送数据的方法postValue() 和 setValue()的调用权限。
LiveData中,方法postValue() 和 setValue()的权限都是protected,因此我们在跨出 LiveData 的package包之后,就无法再直接方便的使用了。
而 MutableLiveData则是将这个权限修改为 public:
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);
}
}
通常,我们在使用MutableLiveData时,初始化过程如下:
MutableLiveData<String> liveData = new MutableLiveData<>();
从前面的源码中我们可以看到,MutableLiveData的构造方法什么也没做,直接调用了父类LiveData的构造方法,LiveData的构造方法如下:
/**
* Creates a LiveData initialized with the given {@code value}.
* @param value initial value
*/
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
/**
* Creates a LiveData with no value assigned to it.
*/
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
在构造方法中,完成了变量的初始化,其中,mData 是一个 volatile关键字修饰的 Object 类型的对象,这个变量在我们使用postValue()方法时非常关键,后面会细说。 mVersion是一个int型的变量。 mData和mVersio具体如下:
private volatile Object mData;
private int mVersion;
然后就是利用 MutableLiveData 的对象 liveData 发送数据,发送数据有两种方式,分别是postValue() 和 setValue()。我们先看看setValue()。
setValue()
public class MutableLiveData<T> extends LiveData<T> {
......
@Override
public void setValue(T value) {
super.setValue(value);
}
}
在这里,MutableLiveData的 setValue()方法也是直接调用父类 LiveData 的set方法。我们进去看看:
// 此方法必须从主线程调用。如果你需要从后台线程设置一个值,则要使用 postValue(value) 方法
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
在这里,我们可以看到该方法被添加了“@MainThread”的注解,确实如注释所说,只能在主线程里面使用。然后方法里面主要做了三件事:
- 调用assertMainThread()方法:鉴定调用该方法的当前线程是否主线程;
- 更新 mVersion 和 mData 两个对象的值;
- 调用 dispatchingValue() 方法开始处理任务。
这里先看看 assertMainThread()方法 :
static void assertMainThread(String methodName) {
if (!ArchTaskExecutor.getInstance().isMainThread()) {
throw new IllegalStateException("Cannot invoke " + methodName + " on a background" + " thread");
}
}
这个很简单,没什么说头。再来看看 dispatchingValue(null) 方法:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// 变量mDispatchingValue初始化时为false;分发Value值的过程中被赋值为true;
// 分发Value值操作完成之后,再次被重置为false;
if (mDispatchingValue) {// mDispatchingValue为true,表示当前正在执行Value值分发操作。
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// setValue()传的 initiator 是null,因此这里执行 else{}分支
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// iterator.next().getValue()获取到的是一个 ObserverWrapper 对象
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
上面的代码,最重要的就是 considerNotify()方法,总前面调用 setValue()方法开始,一路跟踪下来,可以发现,这里的入参 initiator 是null空的。因此执行 else{ ...... }分支。重点不在这里,我们直接分析 considerNotify()方法:
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {// 这里通过判断两次 Version 的值,来消除旧数据的影响。
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData); // 最关键的地方
}
在上面最后一行代码中,onChanged()是不是有点熟悉?在哪里见过的似曾相识之感?不卖关子了,这个方法就是这里的这个:
liveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
// TODO:
}
});
是的,observer.mObserver.onChanged((T) mData)这个调用,最后就是调用到这里来的。
然后就是 开发者 在 onChanged()方法中,处理逻辑。setValue()方法是在主线程中执行,来一次数据,就回调一次数据。不涉及跨线程的调度工作,因此很简单。
关于 observer.mObserver添加监听对象的过程,我们放在最后讨论。
postValue()
postValue()的调用如下:
public class MutableLiveData<T> extends LiveData<T> {
......
@Override
public void postValue(T value) {
super.postValue(value);
}
......
}
LiveData中 postValue()实现如下:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
// mPendingData初始值就是 NOT_SET;之后缓存的是 postValue()
// 方法传进来的 Value值,在完成分发之后,又被重置为 NOT_SET;
postTask = mPendingData == NOT_SET;
// 将要向下分发的值,缓存到 mPendingData
mPendingData = value;
}
// mPendingData当前缓存的值,跟初始值 NOT_SET 不相同,则表示当前正在执行分发操作,不继续向下分发;
// 因为系统会在 mPostValueRunnable 任务中将 mPendingData 重置为NOT_SET;
if (!postTask) {
return;
}
// 切换到主线程,调用 setValue()方法执行分发任务。
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
在这里,我们发现 postValue()方法没有 setValue()方法那样的注解“@MainThread”,说明 postValue()方法的使用不受线程的限制。 在这里的 postValue()方法中,出现了几个重要的变量:
- postTask,这个变量是 postValue()方法的一个关键点,我们稍后细说。
- synchronized:在这里,postValue()方法使用了 synchronized 同步关键字,结合前面的解释,说明 postValue()方法是线程安全的。
- mDataLock:这是一个 final类型的Object对象:final Object mDataLock = new Object();主要作用就是在当前(Mutable)LiveData中,通过synchronized 关键字实现线程安全
- NOT_SET 和 mPendingData:
NOT_SET是一个static + final类型的 Object对象:static final Object NOT_SET = new Object();
mPendingData 是一个volatile 关键字修饰的Object对象:volatile Object mPendingData = NOT_SET; 它保证并发编程时变量的可见性和有序性(禁止指令重排序),不能保证原子性。
可见性的验证:
public class Test1 {
int number = 0;
public void add(){ this.number = 10; }
public static void main(String[] args) {
Test1 test1 = new Test1();
//创建第一个线程
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"开始执行时,number = " + test1.number);
try{ Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}
test1.add(); //暂停3秒后,修改number的值。
System.out.println(Thread.currentThread().getName()+"执行add()方法之后,number = " + test1.number);
},"Thread_One").start();
//第二个是main线程
while (test1.number == 0){ //如果第二个main线程 可以监测到number值的改变,就会跳出当前循环,执行后续程序。 }
System.out.println(Thread.currentThread().getName() + "程序结束!");
}
}
代码会在运行时,卡死在 循环while (test1.number == 0)里,跳不出来。
加上volatile关键字之后:
public class Test2 {
volatile int number = 0;
public void add(){ this.number = 10; }
public static void main(String[] args) {
Test2 test2 = new Test2();
//创建第一个线程
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"开始执行时,number = " + test2.number);
try{ Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}
test2.add();//暂停3秒后,修改number的值。
System.out.println(Thread.currentThread().getName()+"执行add()方法之后,number = " + test2.number);
},"Thread_One").start();
//第二个是main线程
while (test2.number == 0){ // 由于变量number上加了volatile关键字,使得main线程可以监测到number值的改变,从而结束循环。 }
System.out.println(Thread.currentThread().getName()+"程序运行结束!");
}
}
代码会在运行时,从循环while (test1.number == 0)中正常结束。
从代码:postValue()方法的局部代码:
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
可以发现:当 mPendingData 值等于初始值,也就是 NOT_SET 时时,postTask 被设置为true,然后 mPendingData 被赋值为 我们通过postValue()方法传递进来的 value。
当 mPendingData不等于初始值,也就是 mPendingData != NOT_SET时,postTask值为false。
那么 postTask 和 mPendingData 在哪里会用到那? mPendingData又是在哪里被reset为初始值呢?我们继续往下看。
- if(){}判断
if (!postTask) {
return;
}
在这里,postTask值为false,直接return,通过postValue()方法传递的值,直接在这里被拦截掉,不会被传出去。
- ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
这里,执行 mPostValueRunnable 任务,其实本质上就是通过Handle发送消息,以实现跨线程通信,执行任务:
这里,ArchTaskExecutor是 TaskExecutor 的子类,采用了装饰模式实现。postToMainThread()方法的真实实现是在 DefaultTaskExecutor.java中。
@Nullable
private volatile Handler mMainHandler;
@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
mMainHandler = createAsync(Looper.getMainLooper());
}
}
}
mMainHandler.post(runnable);
}
mMainHandler就是一个Handle,调用post方法执行任务。
- mPostValueRunnable任务:
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
在这里,同样是通过 mDataLock 对象来给 mPostValueRunnable任务添加同步锁,上一次的任务还没有执行,这里就先等待。其实这里有一个瑕疵,synchronized同步锁应该将 Object newValue 对象一并包含进去,否则,后面执行 setValue((T) newValue) 时,newValue有报NPE空指针的风险。
在synchronized同步锁里面,先是将 mPendingData 的值赋值给 newValue,然后再将 mPendingData 对象给reset为初始值,也就是 NOT_SET。
最后是调用 setValue((T) newValue),执行任务,这里开始,跟前面 setValue()方法的分析是一样的了。
LiveData通信,setValue()和 postValue()总结起来就是:
setValue():
a、调用assertMainThread()方法鉴定调用该方法的当前线程是否主线程;
b、在considerNotify()方法中,会判断新来的入参值与上一次的参数值的关系,如果是旧的,以前老数据,就不下发了。如果是新数据,就先缓存新的数据,然后再调用
observer.mObserver.onChanged((T) mData)回调,下发数据。
postValue():
a、先在同步方法中判断上一次的数据处理完没有,如果还没有处理完,新的数据就直接被拦截,不下发了。同时,将最新的数据缓存到内存中, 供下次数据发送过来时,判断使用。
b、调用DefaultTaskExecutor.java装饰模式返回来的Handler对象,执行 mPostValueRunnable 任务,实现跨线程通信。
c、在 mPostValueRunnable 任务中,显示将新的值暂存到临时变量 newValue,然后重置变量 mPendingData 为初始值。
d、最后调用 setValue((T) newValue) 方法完成数据分发。