LiveData 连续两次postValue,为啥会丢失第一次的值

起因

同事那出了一个bug,连续两次请求同一接口,传入不同参数,但是livedata的onChange回调只走了一次,导致界面上只有一处更新了…这到底是咋回事呢。

经过

同事提交到自己分支,直接git拉下来,自己调试, 打上log,发现两次postValue都走了,但是LiveData的onChange只走了一次,纳尼?what?
在这里插入图片描述
难道是LiveData里搞鬼了,不应该吧,直接看源码

    volatile Object mPendingData = NOT_SET;
    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET; //1处
            mPendingData = value;//2处
        }
        if (!postTask) {//3处
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;//4处
                mPendingData = NOT_SET;//5处
            }
            setValue((T) newValue);//6处
        }
    };

可以看到这里利用synchronized对一个object对象 mDataLock加了锁,并且维护了一个全局变量mPendingData。
第一次执行,由于mPendingData 初始值为NOT_SET,所以可以得知postTask为true,紧接着给mPendingData赋值第一次要分发的值,然后执行到3处,由于postTask为true,所以继续往下执行,下面在主线程执行一个Runnable
可以看到,在主线程里,我们将mPendingData赋值给newValue,然后将mPendingData重置后,通过setValue进行分发
这里我们就知道,这就是为什么postValue可以在子线程调用,并且一直在主线程更新UI的原因。
断点继续往下走,发现此时又一次执行了postValue,而不是postToMainThread(mPostValueRunnable)里的run方法里,又于此时mPendingData是第一次赋值的值,而不是初始值,所以在1处,postTask为false,在2处,把第二次要分发的值,再次赋值给mPendingData,在3处,由于postTask为false,所以执行return
此时才执行第一次postToMainThread(mPostValueRunnable) mPostValueRunnable对象的run方法里
注意,此时mPendingData里存的是第二次要分发的值
所以newValue为第二次的值,然后mPendingData重置,再执行setValue进行分发

  @SuppressWarnings("WeakerAccess") /* synthetic access */
    void dispatchingValue(@Nullable ObserverWrapper initiator) {
      .....................................
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());//进行分发
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
 @SuppressWarnings("unchecked")
    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);//执行onChanged回调
    }

总结

  • postValue里首先好会将分发的值赋值给mPendingData全局变量,然后将值的分发操作放在一个Runnable里进行,从postValue到 ArchTaskExecutor执行postToMainThread方法(其实里面就是mMainHandler.post(runnable))是有一个时间间隙的。
  • 在这个时间间隙中,由于连续两次postValue,所以mPendingData带着第二次的最新值,进行分发,所以UI刷新,只能看到第二次的值。
    其实刚才分析过程中我们也看到了,由于第二次执行postValue的时候,mPendingData已经不是初始值,所以导致postTask为false,在3处,执行了return。所以虽然执行了两次postValue,可是ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable)只会执行一次。
    既然postValue丢失第一次的值是因为要post mPostValueRunnable对象,而我们看到setValue方法是没有这一步的,所以连续调用两次setValue,LiveData的onChange会执行两次,这也就是postValue和setValue的区别。

思考

问题原因找到了,那进一步思考一下
为什么LiveData要这么设计呢?下面是和一位大佬讨论的结果,记录一下:
LiveData 不该用于做 Event Bus的活,它本身完全也不是为此设计的,反而会造成许多问题,因为LiveData根本不是为 Event Bus 设计的,结果许多开发人员硬要用它做 Event Bus ,然后说 LiveData 有问题并对它做许多 hack。
LiveData 字面意思就是 data 数据,数据不是事件,数据需要满足的几个特性:
1.数据是持续性的
2.可反复读取的
而Event 需要满足的几个特性:
1.事件是非粘滞的,那么是一次性的,不能持久化,且每次事件发生都要不遗漏地通知订阅者
2.如果是粘滞的,需要在订阅者接收后自动取消持久化
3.此外,还需要不能像 LiveData 那样有初始值,即不能初次订阅就立马获得已有值(这不合理,因为订阅时没那事件发生)
所以事件根本不该有数据那样的属性,并且 LiveData postValue 也不会每次都通知到订阅者,而是按照一定间隙时间以最新值通知,这对 Data 是很合理的,但对事件却是 bug 一般的特性,会导致丢失事件,然而这根本不是 LiveData 的问题,因为它根本不该用于 Event
开发者很容易陷入一个误区:什么都要用某种东西来实现
而正确的做法是:用合适的东西做合适的事情,一物专一事。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值