解决【数据倒灌】问题方案二:SingleLiveData:解决LiveData『数据倒灌』的问题(方案二)
1、什么是数据倒灌?
一句话总结就是:先给LiveData设置了value,然后监听者才开始对LiveData进行监听,这时LiveData的value会立马回调给监听者。
虽然从google设计者的角度来看,这并不是一个设计缺陷,但从我们使用者角度来看,其实很多场景下这并不是我们想要的。
我们更期望的是:只收到对LiveData开始监听之后的value,开始监听之前的旧value不要回调给我。
2、数据倒灌的根本原因
LiveData每次setValue或postValue时mVersion都会自增:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
当调用LiveData进行observe时,最终会调到如下方法:
private void considerNotify(ObserverWrapper observer) {
...
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
可以看到,当observer的mLastVersion小于mVersion时就会把之前的数据回调给监听者
另外,observer的mLastVersion的初始值为-1
static final int START_VERSION = -1;
...
int mLastVersion = START_VERSION;
总结:当我们setValue时,mVersion会从-1开始自增,之后我们去observe时,由于observer的mLastVersion的初始值是-1,比mVersion小,所以监听者observe时,会立马把旧的数据回调给监听者。
3、现有解决方案及各自缺陷
Event 事件包装器
对于多观察者的情况,只允许第一个观察者消费,这不符合现实需求;
而且手写 Event 事件包装器,在 Java 中存在 null 安全的一致性问题。
反射干预 Version 的方式
存在延迟,无法用于对实时性有要求的场景;
并且数据会随着 SharedViewModel 长久滞留在内存中得不到释放。
官方最新 demo 中的 SingleLiveEvent
是对 Event 事件包装器 一致性问题的改进,但未解决多观察者消费的问题;
而且额外引入了消息未能从内存中释放的问题。
UnPeekLiveData
提出【数据倒灌】名词大佬写的一个库UnPeekLiveData
缺点:改动大,逻辑复杂,小专栏收费
4、解决方案-UnFlowLiveData
解决的方案有很多,这里介绍一种简单粗暴,侵入性小,一看就懂的方案。
方案思路:
在observe/observeForever时创建新的LiveData,并且根据observer保存该LiveData到mObserverMap中,而且该LiveData订阅相关的observer;
当postValue/setValue时,遍历mObserverMap的所有LiveData,并把值设置给LiveData;
完整代码:
public class UnFlowLiveData<T> {
private final Handler mMainHandler;
private T mValue;
private final ConcurrentHashMap<Observer<? super T>, MutableLiveData<T>> mObserverMap;
public UnFlowLiveData() {
mMainHandler = new Handler(Looper.getMainLooper());
mObserverMap = new ConcurrentHashMap<>();
}
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
checkMainThread("observeForever");
MutableLiveData<T> liveData = new MutableLiveData<>();
// 该LiveData也observeForever该observer,这样setValue时,能把value回调到onChanged中
liveData.observeForever(observer);
mObserverMap.put(observer, liveData);
}
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
checkMainThread("observe");
Lifecycle lifecycle = owner.getLifecycle();
if (lifecycle.getCurrentState() == DESTROYED) {
// ignore
return;
}
lifecycle.addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
mObserverMap.remove(observer);
lifecycle.removeObserver(this);
}
});
MutableLiveData<T> liveData = new MutableLiveData<>();
// 该LiveData也observe该observer,这样setValue时,能把value回调到onChanged中
liveData.observe(owner, observer);
mObserverMap.put(observer, liveData);
}
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
checkMainThread("removeObserver");
mObserverMap.remove(observer);
}
public T getValue() {
return mValue;
}
public void clearValue() {
mValue = null;
}
@MainThread
public void setValue(T value) {
checkMainThread("setValue");
mValue = value;
// 遍历所有LiveData,并把value设置给LiveData
for (MutableLiveData<T> liveData : mObserverMap.values()) {
liveData.setValue(value);
}
}
public void postValue(T value) {
mMainHandler.post(() -> setValue(value));
}
private void checkMainThread(String methodName) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("UnFlowLiveData, Cannot invoke " + methodName
+ " on a background thread");
}
}
}
5、结尾
UnFlowLiveData方案很简单,代码也不多,接口都是保持跟LiveData保持一致,使用起来也方便。这对于既想利用LiveData的优点,又不希望【数据倒灌】的开发者来说,这不失为一个不错的方案。
参考文章
https://blog.csdn.net/ljcITworld/article/details/112849126
https://www.jianshu.com/p/e08287ec62cd
https://www.jianshu.com/p/f3158a4e0bc8
————————————————
版权声明:本文为CSDN博主「疯震震」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hewuzhao/article/details/117165379