1、为什么要把 LiveData 当作事件传递用
利用观察者模式打造的事件总线的优点不必多说(当然也有很多缺点),如 EventBus 和 RxBus 用的好的话能起到很好的解耦作用,使整个程序架构更加清晰,不至于到处传递各种 Callback。但是他们都缺少了对 View 层(Activity、Fragment 等)的生命周期的感知能力,需要在生命周期结束时手动解除观察者,手动管理生命周期十分繁琐且很容易出错。
而 Google 推出的 Lifecycle 库就是为了解决这一问题,其中的 LiveData 就是一个具有生命周期感知能力的观察者,如果用它来打造一个能够感知生命周期的事件总线,岂不美哉!
2、LiveData 当作事件传递用的那些坑
在随着对 LiveData 的运用和理解的逐渐深入,特别是对它的「生命周期感知能力」有了更深的理解,慢慢发现这样用的一些坑,借此机会就跟大家分享探讨一下。而且我平时也有把 LiveData 纯粹当作事件传递来用,特别是列表操作(比如涉及 IO 的增删操作,View 层需要知道哪个数据改动了,以及操作是否成功等,只能以事件的形式传递)。
2.1、postValue 数据丢失的问题
在我的前一篇文章中也提到过,大家也可以直接看源码。postValue 只是把传进来的数据先存到 mPendingData,然后往主线程抛一个 Runnable,在这个 Runnable 里面再调用 setValue 来把存起来的值真正设置上去,并回调观察者们。而如果在这个 Runnable 执行前多次 postValue,其实只是改变暂存的值 mPendingData,并不会再次抛另一个 Runnable。这就会出现后设置的值把前面的值覆盖掉的问题,会导致事件丢失。
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
// 这里先把数据暂存起来,后来的数据会覆盖前面的
mPendingData = value;
}
// 这里保证只抛一个 mPostValueRunnable,#-.-
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
2.2、setValue 不回调观察者
LiveData 的生命周期感知能力就体现在这里,它不会回调处于「非激活状态」(即 onStart 之后到 onPause 之前)的观察者,因为这时更新 View 没有意义,而且比较危险,它会等到观察者激活之后再把新的值回调给他。
但是如果我传了多个数据(假设都是用 setValue 保证不会被覆盖),那些处于非激活状态的观察者是毫不知情的,他们在激活的时候只会收到最后一个数据。这对于事件传递来说,就表现为事件丢失,中间传的任何数据都无法收到,那也就失去了事件传递的意义。
// LiveData 通知观察者时调的函数
private void considerNotify(ObserverWrapper observer) {
// 非激活就直接返回了
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
// 每次更新数据都会版本加一,这里保证不会回调之前发送过的数据
if (observer.mLastVersion >= mVersion) {