调用observe()方法时,第一个参数传入LifecycleOwner对象,而LifecycleOwner能通过getLifecycle()方法获取到lifecycle对象,然后执行lifecycle.addObserver()添加LiveData中数据(mData)变化的观察者对象。
@MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) { if (owner.getLifecycle().getCurrentState() == DESTROYED) { return; } LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } if (existing != null) { return; } owner.getLifecycle().addObserver(wrapper); }
如上图所示,是使用LiveData组件实现数据更新-订阅的开发模式的层级结构。
中的值赋值给mData变量,然后就是走setValue()的流程了。
该类是Observer的包装类,是一个抽象类,具体实现类是LifecycleBoundObserver,内部包裹了mOwner、mObserver、mActive、mLastVersion变量,如下:
private abstract class ObserverWrapper { final Observer super T> mObserver; boolean mActive; // 初始值是-1,LiveData的初始值是0,那么第一次对比时 // observer.mLastVersion肯定是小于mVersion的,一定会更新一次值 int mLastVersion = START_VERSION; }
在ObserverWrapper包装mLastVersion和mObserver的基础上,把LifecycleOwner(中文咋说?生命周期所有者嘛?这不重要,方正不外乎Activty、Fragment、或者自己实现的自定义生命周期组件)也包装了进来,
主要是为了拿到当前Activity/Fragment的生命周期状态,做一些逻辑,比如:onDestroy时要removeObserver,判断当前是否时活跃状态,也就是isAtLeast(STARTED)状态,我们在编写业务代码时也经常传递这个参数给到子模块,完成对于生命周期组件的状态判断、添加、移除生命周期监听的observer对象等逻辑。
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver { @NonNull final LifecycleOwner mOwner; // 将LifecycleOwner对象包装到其中,用户实现一些生命周期的逻辑, // 也能直接拿到lifecycle对象,其实现就是Activity、Fragment。 LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer super T> observer) { super(observer); mOwner = owner; } @Override boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { removeObserver(mObserver); return; } activeStateChanged(shouldBeActive()); } @Override boolean isAttachedTo(LifecycleOwner owner) { return mOwner == owner; } @Override void detachObserver() { mOwner.getLifecycle().removeObserver(this); } }
这是在androidx.core.common库中定义的一个map结构,仔细看下其实是一个链表实现的,实现的是迭代器接口。
private SafeIterableMap, ObserverWrapper> mObservers = new SafeIterableMap<>();
(1)onStart()之后调用setValue()立即回调onChanged(newData)
(2)调用observe()时,如果Observer是新建的实例,那么其绑定的mLastVersion初始值是-1,当调用lifecycleOwner.addObserver()时会调用activeStateChanged(), 然后触发dispatchingValue(), 因为不满足observe.mLastVersion>=mVersion(默认值是0,每次setValue/postValue加1),然后回调onChanged(newData)
如果是在onStop()中注册observer,那么会在回到onStart()后会回调一次onChanged()
⚠️ LiveData中判断LifecycleOwner是判断生命周期状态是否是isAtLeast(START),所以onPause时调用observe()方法也是会回调onChanged()方法的。
(3)LifecycleOwner生命周期变化时,还存在同因生命周期 LiveData其实本质上实现的是将事件发送时机限定在LifecycleOwner的生命周期内的粘性事件。LiveData在生命周期可见时,将不可见时更新的mData版本回传到onChanged()中,当setValue()是在observe()之前调用的,那调用observe()时会把前面setValue的最新的值传给观察者的onChanged()。这个被观察者的变量是否有更新过的逻辑,主要靠LiveData类中定义的mVersion和ObserverWrapper的mLastVersion对比逻辑来实现的。3、说LiveData会数据倒灌是这么回事?
3.1 本质
setValue调用分发value的逻辑很好理解,onActiveStateChanged方法在调用observe()方法增加新观察者时也会调用,然后根据版本决定是否调用observer的onChangd方法。
@SuppressWarnings("unchecked") private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { return; } // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet. // // we still first check observer.active to keep it as the entrance for events. So even if // the observer moved to an active state, if we've not received that event, we better not // notify for a more predictable notification order. if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; observer.mObserver.onChanged((T) mData); }
不使用LiveData进行数据更新时,一般是在UI控件变量准备好了,然后获取数据,再把数据传递给UI控件变量的某个属性,实现UI的更新,这种命令式UI的开发模式本身没有什么问题,符合日常的开发逻辑。
但是因为Android的UI架构都是基于Activity/Fragment生命周期管理的,就必然存在数据异步获取到时界面已经处于不活跃状态的情况。甚至很可能因为内存不足已经销毁了,然后在用户操作下回到活跃状态了,那么异步拿到的数据可能是没有塞给UI控件的,一般都是在onResume时又异步获取一次数据,然后更新到UI控件上。
基于3.1对于LiveData数据更新本质的分析,如果我们使用LiveData的setValue/postValue,然后通过observe()分发进行监听,使用String/Int/CustomEvent(自定义的事件类,类似于EventBus)等数据做为事件分发。虽然这种方式能够避免内存泄漏,但是事件是粘性的,先发送事件,然后注册也会接收到该事件,这样的逻辑实际上并不是我们日常业务的事件逻辑。
google官方sample只处理一次的LiveData事件实现方案:
官方todoapp的Event实现
ps: 不得不说,老外的文章写得还是蛮清楚的,循序渐进,解释各种方案的问题,层层递进,对读者更好的理解有莫大帮助。
这个问题是因为postValue的实现逻辑导致的,如下是postValue的代码:
protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { postTask = mPendingData == NOT_SET; mPendingData = value; } if (!postTask) { return; } ArchTaskExecutor.getInstance().postToMainThread(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); } };
第一次postValue,postTask是true,mPendingData赋值为value,首次调用因为mPending值是NOT_SET,所以postTask为true, 会调用postToMainThread方法。
然后同步短时间内再次调用postValue,这时候因为mPostValueRunnable中的逻辑还未执行,所以mPendingData会赋值为新的value值,但是这次因为mPending不再是NOT_SET,而是第一次调用的值了,所以postTask为false,postValue方法直接return了。
然后在mPostValueRunnable.run方法被执行到时,mPendingData已经是最后一次调用postValue时传入的值了,所以mPostValueRunnable中调用的setValue方法,最终回调到Observer#onChanged的是最后一次postValue传入的值。
// 按钮点击是,连续多次执行postValue simpleViewModel.liveData1.postValue("我是新数据0") simpleViewModel.liveData1.postValue("我是新数据1") simpleViewModel.liveData1.postValue("我是新数据2") simpleViewModel.liveData1.postValue("我是新数据3") // onChanged回调的结果只有最后一次 23:56:22.732 D liveData1: onChanged newValue = 我是新数据3 // 如果给两次postValue之间加间隔呢? simpleViewModel.viewModelScope.launch(Dispatchers.IO) { simpleViewModel.liveData1.postValue("我是新数据0") delay(10) simpleViewModel.liveData1.postValue("我是新数据1") delay(10) simpleViewModel.liveData1.postValue("我是新数据2") delay(10) simpleViewModel.liveData1.postValue("我是新数据3") } // 输出结果如下,就没有出现丢失数据的更新的情况了。并且尝试把间隔时间改成1ms,多次操作会出现可能丢失部分数据, // 可能全部不丢失的,这里能说明上面关于postValue短时间内连续更新数据,可能只有最后一次分发给观察者的原因了。 // !!这里也能说明,使用LiveData实现事件分发,要也别注意异步分发事件可能丢事件的。 // 这里也说明了LiveData在异步数据流上是存在缺陷的,当然google又出了Flow组件专门用于处理数据流,待后续会分享其用法。 00:03:29.143 D liveData1: onChanged newValue = 我是新数据0 00:03:29.199 D liveData1: onChanged newValue = 我是新数据1 00:03:29.212 D liveData1: onChanged newValue = 我是新数据2 00:03:29.232 D liveData1: onChanged newValue = 我是新数据3
(1)数据粘性更新;
(2)异步更新数据的postValue可能丢失更新过程中的值,不适用于异步数据流的更新&展示。
比如:要实现进度的展示,用LiveData#postValue就可能出现进度变化过程丢一部分了,UI要的一些变化过程没了(貌似这个举例不妥~),作为事件总线分发事件可能丢失等情况;
【1】LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)