jitpack之livedata倒灌现象

153 阅读2分钟

一、背景

我想在界面弹出toast,这个toast的内容我保存在ViewModel中的一个LiveData变量中:

val toastMsg = MutableLiveData<String>()

然后在网络加载失败时给它赋值:

fun getUpdateInfo() {
    launchOnUI {
        _updateData.value = NetworkRequestUIState(isLoading = true)
        val result = mineRepository.getUpdateInfo("LowCarbon")
        if (result is ResponseResult.Error) {
            _updateData.value =
                NetworkRequestUIState(isLoading = false, isError = result.errorMsg)
            //给toastMsg赋值
            toastMsg.value = "获取更新失败"
        } else if (result is ResponseResult.Success) {
            _updateData.value =
                NetworkRequestUIState(isLoading = false, isSuccess = result.data)
        }
    }
}

接着在View层进行observe,代码如下:

//toast信息
toastMsg.observe(this@MineActivity){
    if (it.isNotEmpty()){
        Toast.makeText(this@MineActivity, it, Toast.LENGTH_SHORT).show()
    }
}

这里当竖屏时,网络不好时,会弹出toast;然后进行横屏后,会发现又弹了一次toast,这说明 "获取更新失败" 这个事件又被消费了一次,这明显不符合逻辑,那为什么会又弹出一次呢。

二、原因

这个原因其实很简单,当配置发生变化时:

  • Activity会重新走一遍生命周期函数,而这时会再次添加观察者,调用observer方法,方法内会new一个新的ObserverWrapper对象,就会有一个新的 mLastVersion = -1。

  • 当Activity重新活跃,会再次执行事件分发。

    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            //当Activity重建时,生命周期会重新走一遍
            Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
            if (currentState == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            Lifecycle.State prevState = null;
            //上一次的state跟当前的不同时,执行事件分发
            while (prevState != currentState) {
                prevState = currentState;
                //当走到ON_RESUME时会触发页面活跃,会notify数据
                activeStateChanged(shouldBeActive());
                currentState = mOwner.getLifecycle().getCurrentState();
            }
        }
    }
    
  • 在事件分发时,会判断 mLastVersion >= mVersion,就分发失败;反之,如果分发事件成功,就将当前LiveData的mVersion赋值给mLastVersion。很明显这里mLastVersion是一个新初始化的对象,初始值为-1,这里就是数据倒灌的原因。

    // 当前宿主Activity的mLastVersion大于等于LiveData的mVersion,不分发
    // 这里就是数据倒灌的原因
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    

三、解决方案

再来回顾下,数据倒灌的常见场景:

  • 屏幕旋转
  • 用户手动切换系统语言

解决方案:

  • 如果应用不需要横屏,就设置为永久竖屏。
  • 官方扩展的SingleLiveEvent。
  • 美团反射修改mVersion。
  • UnPeek-LiveData。