文章目录
一、问题描述:
最近面试天天被虐,有个问题问的很频繁,就是 LiveData 的数据倒灌问题怎么解决。
我不知道有多少人连数据倒灌是什么都没听过的,更不要说什么解决方案啦。
我按照我的理解描述一下数据倒灌:就是设置了 LiveData 的数据之后,再观察 LiveData,这时候拿到的数据是观察之前设置的数据,用比较难懂的说法就是之前设置的数据倒灌过来了。越说越乱了,其实就是一个粘性事件,不管你什么时候观察,都可以拿到最后设置的数据。
举个例子:我把接口返回的错误信息保存到一个 LiveData 中,在当前 Fragment 的 onViewCreated 中绑定,观察到错误信息后弹出了提示框,我关闭了提示框,跳到了另一个 Fragment,然后再返回刚才那个 Fragment,奇迹发生了,它又弹窗了!!!
二、先分析一下LiveData源码:
这里看的 LiveData 版本是2.5.1。
1、创建LiveData对象:
- 创建一个LiveData对象。
- LiveData对象维护着一份数据,mData,Object类型,也就是我们setValue保存的数据,默认为NOT_SET,一个静态对象。
- LiveData对象维护着一个版本号mVersion,默认是-1,每次setValue后都会+1。
// 这样是正常写法吧
val liveData = MutableLiveData<String>()
// 类:LiveData
// 这个是LiveData的构造方法
public LiveData() {
// mData就是LiveData保存的最后一次更新的数据
// private volatile Object mData;
// static final Object NOT_SET = new Object();
mData = NOT_SET;
// 这个是LiveData的数据版本号,每一次更新数据版本号都会+1
// private int mVersion;
// static final int START_VERSION = -1;
mVersion = START_VERSION;
}
2、观察LiveData对象:
- 传入用户定义的观察者Observer,包装成LifecycleBoundObserver,保存到mObservers里面。
- 每一个Observer都保存着一个版本号:mLastVersion,默认是-1,每次消费后都更新成LiveData的版本号。
- Observer绑定生命周期。
// 是这样观察吧
liveData.observe(lifeCycleOwner) {
println(it)
}
// 执行:liveData.observe(lifeCycleOwner) { println(it) }
// 类:LiveData
public void observe(LifecycleOwner owner, Observer<? super T> observer) {
// 把生命周期和观察者绑定起来,构造方法在下面
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 所有观察者保存到mObservers里面
// private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();
// 如果mObservers已经存在wrapper,则返回
// 如果mObservers不存在wrapper的话,则put进去,返回null
// 所以最终mObservers里面保存着所有的观察者
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 我们这里只考虑第一次添加观察者的情况,直接绑定生命周期
// 至此,观察者已经添加完毕
owner.getLifecycle().addObserver(wrapper);
}
// 执行:new LifecycleBoundObserver(owner, observer);
// 类:LifecycleBoundObserver
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
}
// 执行:super(observer);
// 类:ObserverWrapper
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
int mLastVersion = START_VERSION;// START_VERSION = -1
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
}
3、进入生命周期:
ObserverWrapper里面有一个mActive变量,如果没有进入生命周期,mActive默认是false的。
从人类角度思考的话,就是某个观察者,如果它绑定的生命周期没有进入到STARTED 状态的话,是不会激活的,没激活的话就不会观察到任何东西。
- Observer绑定生命周期后,回调 onStateChanged 方法。
- 如果生命周期状态变成STARTED以上,则调用dispatchingValue(this)分发数据(这个this就是Observer,意思是给这个Observer分发)。
// 类:LifecycleBoundObserver(继承LifecycleEventObserver)
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// DESTROYED状态,移出观察者
// 从这里可以看出LiveData不需要手动解除观察者,都是自动的
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
// 这里加了个prevState,确保在状态有变化之后才处理
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
// shouldBeActive(),至少是STARTED状态以上才会返回true
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
// 执行:activeStateChanged(shouldBeActive());
// 类:ObserverWrapper
void activeStateChanged(boolean newActive) {
// 我们只考虑正常情况,newActive=true
// mActive默认为false
if (newActive == mActive) {
return;
}
mActive = newActive;
if (mActive) {
// 分发,具体看setValue()部分,传入了具体的观察者
// 这个代码是在ObserverWrapper里面的,这个this就是观察者,
// 意思就是向这个观察者分发数据
dispatchingValue(this);
}
}
4、LiveData.setValue():
- LiveData的版本号mVersion+1。
- 数据保存一份到mData。
- 调用dispatchingValue(null)分发数据,传入null表示给所有Observer分发。
- 如果Observer的版本号大于等于LiveData的版本号,则不分发,因为已经分发过了。
- 如果分发,把Observer的版本号更新为LiveData的版本号,回调Observer的onChanged(mData)方法。
// kotlin:setValue()
liveData.value = "hello"
// 类:LiveData
protected void setValue(T value) {
// 每次setValue版本号都会+1,postValue最终也是setValue
mVersion++;
// 保存最后更新的数据
mData = value;
// 给观察者们分发数据
// 没有传入具体的观察者,而是传入了null,表示给所有观察者分发
dispatchingValue(null);
}
// 执行:dispatchingValue(null);
// 类:LiveData
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
// 如果传入了具体的观察者,则直接调用considerNotify
// 绑定观察者的时候会马上分发
considerNotify(initiator);
initiator = null;
} else {
// 没有传入具体的观察者,则遍历mObservers,拿到每一个观察者执行considerNotify
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
// 执行:considerNotify(iterator.next().getValue());
// 类:LiveData
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// 我们只考虑分发的情况
// mLastVersion默认是-1
// 这里很重要,
// 观察者每次收到数据后都会把自己的版本号设置成LiveData的版本号
// 所以当观察者的版本号大于等于LiveData的版本号,
// 那就说明这个观察者已经处理过这个版本的数据了
if (observer.mLastVersion >= mVersion) {
return;
}
// 每个观察者也会保存一份自己的版本号
observer.mLastVersion = mVersion;
// 至此回调用户定义的观察者,收工
observer.mObserver.onChanged((T) mData);
}
5、LiveData.postValue():
- 把数据post到主线程,调用setValue。
// 使用:
liveData.postValue("hello")
// 类:LiveData
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
// mPendingData默认是NOT_SET
// static final Object NOT_SET = new Object();
// volatile Object mPendingData = NOT_SET;
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
// 通过hanlder提交到主线程执行(所有需要主线程跑的代码全部都是通过handler提交的)
// 很多JectPack的库都有用到这个ArchTaskExecutor
// 我们自己的代码也可以直接用它,也可以给它设置我们自己的线程池
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
// 这里回到setValue()的情况
setValue((T) newValue);
}
};
三、粘性事件和数据倒灌:
从上面分析可以看到LiveData里面保存的上一次分发的数据mData,这是一个Object对象,并且在分发完毕后不会置空,所以后来的观察者也能观察到这个对象。
解决思路:
不要解决!!!
LiveData 本来就是这么设计的,是用来保存数据的,不是用来分发事件的。
解决思路参考:
1、反射。我看很多博客都说可以用反射,在 observe 的时候反射拿到 LiveData 的版本号,再反射赋值给 Observer 的版本号,但是,从理论上分析我就觉得不可能。首先 LifecycleBoundObserver 是在 observe 方法中生成的,通过反射根本拿不到这个对象。但是可以从 mObservers 中拿到,然而是在 super.observe(owner, observer) 之后才能拿到,这时候已经绑定生命周期并且触发分发了,拿到 Observer 还有什么意义。
2、https://github.com/KunMinX/UnPeek-LiveData 这个库看着可行。大概原理就是,自己定义一个 MyLiveData 和 MyObserver,然后自己维护一份 MyLiveData 和 MyObserver 的版本号,在创建 MyObserver 的时候把 MyObserver 的版本号设置成 MyLiveData 的版本号,在 MyObserver 的 onChanged 方法中判断 MyLiveData 的版本号大于等于 MyObserver 的版本号才执行。和前面反射的原理其实是差不多的,只是不反射了,而是自己维护一套版本号。
3、可以用 SharedFlow
replay设成0,完美替代。
[注:以上均为个人理解,如有错误,望指正]
[注:以上源码为了逻辑清晰可能会有小改或删减]