LiveData源码分析,粘性事件,数据倒灌

最近面试天天被虐,有个问题问的很频繁,就是 LiveData 的数据倒灌问题怎么解决。

我不知道有多少人连数据倒灌是什么都没听过的,更不要说什么解决方案啦。

我按照我的理解描述一下数据倒灌:就是设置了 LiveData 的数据之后,再观察 LiveData,这时候拿到的数据是观察之前设置的数据,用比较难懂的说法就是之前设置的数据倒灌过来了。越说越乱了,其实就是一个粘性事件,不管你什么时候观察,都可以拿到最后设置的数据。

举个例子:我把接口返回的错误信息保存到一个 LiveData 中,在当前 Fragment 的 onViewCreated 中绑定,观察到错误信息后弹出了提示框,我关闭了提示框,跳到了另一个 Fragment,然后再返回刚才那个 Fragment,奇迹发生了,它又弹窗了!!!

先分析源码:

以下的代码都是部分关键代码,一些不想看的看不懂的代码我直接删掉了,,,

创建LiveData对象:

java 复制代码
// 这样是正常写法吧
val liveData = MutableLiveData<String>()

// 类:LiveData
// 这个是LiveData的构造方法
public LiveData() {
	// mData就是LiveData保存的最后一次更新的数据
    // private volatile Object mData;
    // static final Object NOT_SET = new Object();
    mData = NOT_SET;

	// 这个是LiveData的数据版本号,每一次更新数据版本号都会+1
    // private int mVersion;
    // static final int START_VERSION = -1;
    mVersion = START_VERSION;
}

观察LiveData对象:

java 复制代码
// 是这样观察吧
liveData.observe(lifeCycleOwner) { 
    println(it) 
}

// 执行:liveData.observe(lifeCycleOwner) { println(it) }
// 类:LiveData
public void observe(LifecycleOwner owner, Observer<? super T> observer) {
    // 把生命周期和观察者绑定起来,构造方法在下面
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    
    // 所有观察者保存到mObservers里面
    // private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();
    // 如果mObservers已经存在wrapper,则返回
    // 如果mObservers不存在wrapper的话,则put进去,返回null
    // 所以最终mObservers里面保存着所有的观察者
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    
    // 我们这里只考虑第一次添加观察者的情况,直接绑定生命周期
    // 至此,观察者已经添加完毕
    owner.getLifecycle().addObserver(wrapper);
}

// 执行:new LifecycleBoundObserver(owner, observer);
// 类:LifecycleBoundObserver
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }
}

// 执行:super(observer);
// 类:ObserverWrapper
private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;

    ObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }
}

进入生命周期:

ObserverWrapper里面有一个mActive变量,如果没有进入生命周期,mActive默认是false的。 从人类角度思考的话,就是某个观察者,如果它绑定的生命周期没有进入到STARTED 状态的话,是不会激活的,没激活的话就不会观察到任何东西。 生命周期变化会回调onStateChanged 方法:

java 复制代码
// 类:LifecycleBoundObserver(继承LifecycleEventObserver)
public void onStateChanged(@NonNull LifecycleOwner source,
        @NonNull Lifecycle.Event event) {
    // DESTROYED状态,移出观察者
    // 从这里可以看出LiveData不需要手动解除观察者,都是自动的
    Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
    if (currentState == DESTROYED) {
        removeObserver(mObserver);
        return;
    }

    // 这里加了个prevState,确保在状态有变化之后才处理
    Lifecycle.State prevState = null;
    while (prevState != currentState) {
        prevState = currentState;
        // shouldBeActive(),至少是STARTED状态以上才会返回true
        activeStateChanged(shouldBeActive());
        currentState = mOwner.getLifecycle().getCurrentState();
    }
}

// 执行:activeStateChanged(shouldBeActive());
// 类:ObserverWrapper
void activeStateChanged(boolean newActive) {
    // 我们只考虑正常情况,newActive=true
    // mActive默认为false
    if (newActive == mActive) {
        return;
    }
    mActive = newActive;
    if (mActive) {
        // 分发,具体看setValue()部分,传入了具体的观察者
        // 这个代码是在ObserverWrapper里面的,这个this就是观察者,
        // 意思就是向这个观察者分发数据
        dispatchingValue(this);
    }
}

LiveData.setValue():

java 复制代码
// kotlin:setValue()
liveData.value = "hello"

// 类:LiveData
protected void setValue(T value) {
    // 每次setValue版本号都会+1,postValue最终也是setValue
    mVersion++;
    // 保存最后更新的数据
    mData = value;
    // 给观察者们分发数据
    // 没有传入具体的观察者,而是传入了null,表示给所有观察者分发
    dispatchingValue(null);
}

// 执行:dispatchingValue(null);
// 类:LiveData
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            // 如果传入了具体的观察者,则直接调用considerNotify
            // 绑定观察者的时候会马上分发
            considerNotify(initiator);
            initiator = null;
        } else {
            // 没有传入具体的观察者,则遍历mObservers,拿到每一个观察者执行considerNotify
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

// 执行:considerNotify(iterator.next().getValue());
// 类:LiveData
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }

    // 我们只考虑分发的情况
    // mLastVersion默认是-1
	// 这里很重要,
	// 观察者每次收到数据后都会把自己的版本号设置成LiveData的版本号
	// 所以当观察者的版本号大于等于LiveData的版本号,
	// 那就说明这个观察者已经处理过这个版本的数据了
    if (observer.mLastVersion >= mVersion) {
        return;
    }

	// 每个观察者也会保存一份自己的版本号
    observer.mLastVersion = mVersion;

    // 至此回调用户定义的观察者,收工
    observer.mObserver.onChanged((T) mData);
}

LiveData.postValue():

java 复制代码
// 使用:
liveData.postValue("hello")

// 类:LiveData
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        // mPendingData默认是NOT_SET
        // static final Object NOT_SET = new Object();
        // volatile Object mPendingData = NOT_SET;
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    
    // 通过hanlder提交到主线程执行(所有需要主线程跑的代码全部都是通过handler提交的)
    // 很多JectPack的库都有用到这个ArchTaskExecutor
    // 我们自己的代码也可以直接用它,也可以给它设置我们自己的线程池
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        // 这里回到setValue()的情况
        setValue((T) newValue);
    }
};

粘性事件和数据倒灌

从上面分析可以看到LiveData里面保存的上一次分发的数据mData,这是一个Object对象,并且在分发完毕后不会置空,所以后来的观察者也能观察到这个对象。

解决思路:

不要解决!!!

LiveData 本来就是这么设计的,是用来保存数据的,不是用来分发事件的。

解决思路参考:

1、反射。我看很多博客都说可以用反射,在 observe 的时候反射拿到 LiveData 的版本号,再反射赋值给 Observer 的版本号,但是,从理论上分析我就觉得不可能。首先 LifecycleBoundObserver 是在 observe 方法中生成的,通过反射根本拿不到这个对象。但是可以从 mObservers 中拿到,然而是在 super.observe(owner, observer) 之后才能拿到,这时候已经绑定生命周期并且触发分发了,拿到 Observer 还有什么意义。

2、github.com/KunMinX/UnP... 这个库看着可行。大概原理就是,自己定义一个 MyLiveData 和 MyObserver,然后自己维护一份 MyLiveData 和 MyObserver 的版本号,在创建 MyObserver 的时候把 MyObserver 的版本号设置成 MyLiveData 的版本号,在 MyObserver 的 onChanged 方法中判断 MyLiveData 的版本号大于等于 MyObserver 的版本号才执行。和前面反射的原理其实是差不多的,只是不反射了,而是自己维护一套版本号。

3、其他的都不用考虑了。

相关推荐
用户060905255221 小时前
Compose 主题 MaterialTheme
android jetpack
用户060905255221 小时前
Compose 简介和基础使用
android jetpack
用户060905255222 小时前
Compose 重组优化
android jetpack
行墨4 小时前
Jetpack Compose 深入浅出(一)——预览 @Preview
android jetpack
alexhilton2 天前
突破速度障碍:非阻塞启动画面如何将Android 应用启动时间缩短90%
android·kotlin·android jetpack
Pika3 天前
深入浅出 Compose 测量机制
android·android jetpack·composer
fundroid4 天前
掌握 Compose 性能优化三步法
android·android jetpack
ljt27249606618 天前
Compose笔记(五十一)--rememberTextMeasurer
android·笔记·android jetpack
wxson728213 天前
【用androidx.camera拍摄景深合成照片】
kotlin·android jetpack·androidx
天花板之恋13 天前
Compose Navigation总结
android jetpack