欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

注意避坑:LiveData“不起眼”又会遇到的问题 (Tips)

程序员文章站 2022-04-15 18:05:49
PostValue() 会丢弃老的值某天我在编写一段逻辑时这样使用了 LiveData 来传数据:textLiveData.postValue("1")textLiveData.postValue("2")结果在回调处只回调了 "2",那么 "1" 去哪了!看一下 postValue 的源码: protected void postValue(T value) { boolean postTask; synchronized (mDataLoc...

PostValue() 会丢弃老的值

某天我在编写一段逻辑时这样使用了 LiveData 来传数据:

textLiveData.postValue("1")
textLiveData.postValue("2")

结果在回调处只回调了 "2",那么 "1" 去哪了!

看一下 postValue 的源码:

    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
                // 如果 mPendingData 已经有值,postTask 为 false,
            // 就不会执行下面的 postToMainThread 任务
            postTask = mPendingData == NOT_SET;
            
            // 每次 post 这里的 mPendingData 都会刷新,
            // 所以我们总是能得到最新值的回调
            mPendingData = value;
        }

        if (!postTask) {
            return;
        }
        
        // 将任务 post 到主线程 Handler 执行
                ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
    private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                    // 设置值之后将  mPendingData 重置
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            // 最终还是调的 setValue
            setValue((T) newValue);
        }
    };

postValue 是把值 post 到主线程来执行,而我们的主线程是取队列里的消息一个接一个处理的,所以处理不会那么及时。分析一下上面的场景,先 post 了一个 "1",此时 mPendingData = "1"postTask = true,此任务被 post 给主线程等待执行。紧接着又 post "2",此时 mPendingData = "2"postTask = false,只会修改一下 mPendingData 的值,而主线程的任务在 mPendingData 修改为 "2" 之后才被执行。所以我们得到的回调总是最后一个。

总结: postValue 其实是为多线程环境设计的,普通的场景没有必要使用它。直接使用 setValue 就行。

生命周期重复回调导致的 LiveData 回调重复

之前我把 LiveData 用来在 Activity 和 Fragment 之间传递点击事件。在 ViewPager 里的 fragment 中使用,一下就炸了,左右切换 Fragment 的时候就触发了点击事件。

由于 LiveData 在组件初始化活跃时总是回去检查是否有值,有值就会回调。所以如果一个 Fragment 注册了 LiveData 的回调,Activity 反复移除再添加 Frgment, 就会回调多次 LiveData 的回调。(注:这里的 LiveData 存在于 Activity 的 ViewModel 中,生命周期比 Fragment 长)

之前我的解决方案是在 fragment onDestory 的时候将 LivaData 置为 null。其实不够优雅,这种短暂性的消息,用完就没有意义的可以封装一下 LiveData:

发送完消息立即置为空,一个转瞬即逝的 LiveData,值不会被保存。这里需要做好对 null 的判断: if(v == null) return

class FleetingLiveData<T> : MutableLiveData<T>() {

    override fun setValue(value: T) {
        super.setValue(value)
        super.setValue(null)
    }

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner, Observer {
            if (it == null) return@Observer
            observer.onChanged(it)
        })
    }
}

总结:LiveData 和 组件生命周期绑定,有值就会回调的特性有利有弊。可以用来做粘性事件,但是用不好也会发生奇奇怪怪的回调,比如用来传递点击事件这种不需要保存值的短暂事件。


看完点赞,养成习惯,微信搜一搜「 程序猿养成中心 」关注这个喜欢写干货的程序员。

另外更有Android一线大厂面试完整考点、资料更新在我的Gitee,有面试需要的朋友们可以去参考参考,如果对你有帮助,可以点个Star哦!

Gitee地址:【 老陈的Gitee


 

本文地址:https://blog.csdn.net/qq_39477770/article/details/108851836