Open zgq105 opened 2 years ago
在lifecycle篇章中说过livedata监听Activity生命周期来控制livedata中的激活状态,如果处于激活状态才通知观察者数据变更。接下来通过分析setValue和postValue的过程来剖析Livedata监听数据变更的全过程。
平时我们经常会通过 mutableLiveData.value = "zgq"的方式去更新数据和通知。接下来看看setValue中做了哪些操作,如下:
@MainThread protected void setValue(T value) { assertMainThread("setValue");//需要在主线程调用 mVersion++; //每次赋值版本号+1 mData = value; //赋值,mData是volatile修饰的Object类型的变量,保证多线程的可见性和防止指令重排 dispatchingValue(null); //业务分发 }
void dispatchingValue(@Nullable ObserverWrapper initiator) { //... for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); //遍历所有的观察者,通知数据有变化 if (mDispatchInvalidated) { break; } } }
通知观察者considerNotify,如下:
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); //通知外面业务数据变更 }
平时我们经常会通过 mutableLiveData.postValue("zgq")的方式去更新数据和通知。接下来看看postValue中做了哪些操作,如下:
protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { //加锁保证线程安全 postTask = mPendingData == NOT_SET; mPendingData = value; //mPendingData是volatile修饰的Object类型,保证线程可见性 } if (!postTask) { return; } ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); //这个方法最终转到主线程中调用,通过Handler 的方式 }
postToMainThread的内部实现是在DefaultTaskExecutor类中,如下:
@Override public void postToMainThread(Runnable runnable) { if (mMainHandler == null) { synchronized (mLock) { if (mMainHandler == null) { mMainHandler = createAsync(Looper.getMainLooper()); } } } //noinspection ConstantConditions mMainHandler.post(runnable); //主线程Handler }
接下来看下mPostValueRunnable的内部实现,如下:
private final Runnable mPostValueRunnable = new Runnable() { @SuppressWarnings("unchecked") @Override public void run() { Object newValue; synchronized (mDataLock) { newValue = mPendingData; mPendingData = NOT_SET; } setValue((T) newValue); //最终调用还是调用到setValue方法 } };
总结:setValue是在要求主线程中调用;postValue是异步调用的,通常适用于子线程调用。
viewModel.testData.value = "hello world" viewModel.testData.observe(viewLifecycleOwner) { Log.d("zgq", "testData:$it") }
上面这段代码会输出testData:hello world,因此发生了数据倒灌的问题,即把旧的数据发送给新添加的观察者。
原因分析: 因为LiveData内部使用的是版本号机制,mVersion是LiveData类的全局变量,默认是0或者-1,取决于调用的构造函数。 ObserverWrapper类中的全局变量mLastVersion默认是-1。根据下面的逻辑判断:是会触发数据倒灌:
private void considerNotify(ObserverWrapper observer) { //省略 //第一次observer.mLastVersion是-1,而mVersion是1或者0,因为setValue是mVersion执行了++的操作。所以触发了旧数据通知到新的观察者的情况 if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; observer.mObserver.onChanged((T) mData); }
解决方式很多,可以参考:链接
1.发起网络请求,然后界面切后台,接口返回数据,livedata数据更新了,此时界面的livedata观察者会立即收到数据吗?
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver { @Override boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); //至少STARTED之后才认为是活跃状态 } @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState(); if (currentState == DESTROYED) { removeObserver(mObserver); return; } Lifecycle.State prevState = null; while (prevState != currentState) { prevState = currentState; activeStateChanged(shouldBeActive()); //生命周期状态改变,通知状态变更 currentState = mOwner.getLifecycle().getCurrentState(); } } }
void activeStateChanged(boolean newActive) { if (newActive == mActive) { return; } // immediately set active state, so we'd never dispatch anything to inactive // owner mActive = newActive; changeActiveCounter(mActive ? 1 : -1); if (mActive) { //如果是活跃状态 dispatchingValue(this); //分发出去通知所有观察者 } }
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); }
结论:以上情景,切到后台之后观察者并不会马上收到接口数据的变更,只有再次切换回前台才会通知观察者。
在lifecycle篇章中说过livedata监听Activity生命周期来控制livedata中的激活状态,如果处于激活状态才通知观察者数据变更。接下来通过分析setValue和postValue的过程来剖析Livedata监听数据变更的全过程。
1.setValue方法
平时我们经常会通过 mutableLiveData.value = "zgq"的方式去更新数据和通知。接下来看看setValue中做了哪些操作,如下:
通知观察者considerNotify,如下:
2.postValue方法
平时我们经常会通过 mutableLiveData.postValue("zgq")的方式去更新数据和通知。接下来看看postValue中做了哪些操作,如下:
postToMainThread的内部实现是在DefaultTaskExecutor类中,如下:
接下来看下mPostValueRunnable的内部实现,如下:
总结:setValue是在要求主线程中调用;postValue是异步调用的,通常适用于子线程调用。
3.livedata数据倒灌问题(粘性问题)
上面这段代码会输出testData:hello world,因此发生了数据倒灌的问题,即把旧的数据发送给新添加的观察者。
原因分析: 因为LiveData内部使用的是版本号机制,mVersion是LiveData类的全局变量,默认是0或者-1,取决于调用的构造函数。 ObserverWrapper类中的全局变量mLastVersion默认是-1。根据下面的逻辑判断:是会触发数据倒灌:
4.livedata数据倒灌问题解决方案
解决方式很多,可以参考:链接
5.常见问题分析:
1.发起网络请求,然后界面切后台,接口返回数据,livedata数据更新了,此时界面的livedata观察者会立即收到数据吗?
结论:以上情景,切到后台之后观察者并不会马上收到接口数据的变更,只有再次切换回前台才会通知观察者。