简单粗暴解决LiveData『数据倒灌』的问题

解决【数据倒灌】问题方案二: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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值