内容简介:引入androidx后,ViewModel+LiveData搭配Activity/Fragment渐渐成为大家喜爱(习惯)的UI制作方式。总体来说,这套模式的容易学习,使用也方便,但如果没有详细了解其背后的工作机制,也很容易错误使用造成bug。在此将分享相关architecture component背后原理,将实际开发中遇到的问题和错误总结一些good practice points,以供参考。
引入androidx后,ViewModel+LiveData搭配Activity/Fragment渐渐成为大家喜爱(习惯)的UI制作方式。
总体来说,这套模式的容易学习,使用也方便,但如果没有详细了解其背后的工作机制,也很容易错误使用造成bug。
在此将分享相关architecture component背后原理,将实际开发中遇到的问题和错误总结一些good practice points,以供参考。
这篇文章主要探讨LiveData的 工作原理 和 基本使用方式 。
How it works:
- LiveData::setValue VS LiveData::postValue
- LiveData::observe(lifecycleOwner, observer)
Good Practice Points:
-
Activity观察LiveData,在onCreate注册观察,
livedata.observe(this, Observer<T>{})
-
Fragment观察LiveData,在onCreateView/onViewCreated/onActivityCreated 注册观察,
livedata.observe(viewLifeCycleOwner, Observer<T>{})
- 不可将 LiveData直接当作EventBus
-
(Tip)建立一个MutableLiveData实例时,可以
MutableLiveData<T>()
,无需要造一个无意义的初始值null/emptylist,避免观察到无意义的值。
LiveData::setValue vs LiveData::postValue
更新一个MutableLiveData实例中值,需要通过setValue或postValue方法,其中setValue只能在主线程调用。为什么有这样的设计呢?
阅读LiveData(MutableLiveData的父类)源码,其内部使用了俩个volatile修饰的成员变量,mData和mPendingData。
mData保存了最终数据。LiveData实例暴露给外部取值的getValue方法,以及其内部推送数据给观察者时,使用的都是mData。mData只能通过setValue方法更新,即只能由main thread写入,加上volatile的特性(直写main memory而非cpu cache),写入的新值将对所有线程可见。所以不会出现mData更新后,还有线程读取到更新前数据的情况。
那主线程外更新数据怎么实现呢?就由postValue利用mPendingData完成。
mPendingData中保存了将要但还未被写入mData中的值。postValue方法中,新的数据先被写入mPendingData,然后post一个runnable task到主线程。task中调用setValue方法将mPendingData中的值写入mData,接着清除mPendingData中的值。在postValue和task中,mPendingData的读写都由synchronized block包裹。即postValue中和task中对mPendingData的操作不会并行,避免了postValue对mPendingData的赋值正好被task中mPendingData清除覆盖的情况。
postValue source code
protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { postTask = mPendingData == NOT_SET; mPendingData = value; } if (!postTask) { return; } ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); }
另外,postValue时,如果当前已有task就不会再post新的task。所以task被执行前,无论postValue被调用多少次,只有最后一次postValue中传入的值,会被更新入mData。也就是说,观察者们不会观察到之前多次postValue中的数据。
task source code
private final Runnable mPostValueRunnable = new Runnable() { @SuppressWarnings("unchecked") @Override public void run() { Object newValue; synchronized (mDataLock) { newValue = mPendingData; mPendingData = NOT_SET; } setValue((T) newValue); } };
所以,LiveData 不能直接 被当作 事件线 使用。它的事件是会“丢失”的。它更像是Rx中的BehaviorSubject而非Observerable。除了上述原因,观察者从inActive状态切换到active状态时,会“主动”观察当前LiveData中的值,也是LiveData不可作事件线用的另一个原因。
LiveData::observe
在activity/fragment中观察LiveData,我们一般都会创建一个observer实例,调用liveData.observe(lifecycleOwner, observer)方法,在observer的回调方法onChanged中,实现UI的更新,无需再判断当前lifecycle来决定是否要更新。
observe source code
@MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { assertMainThread("observe"); if (owner.getLifecycle().getCurrentState() == DESTROYED) { // ignore 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的成员变量mObservers保存了所有observer和它们各自的相关的lifecycleowner。
在observe方法中,observer和lifecycleOwner包裹成一个新的lifecycleBoundObserver对象,在检查有效性后,observer和包裹后的lifecycleBoundObserver分作为key、value存入mObservers中。
lifecycleBoundObserver实现了LifecycleEventObserver接口。
执行lifecycle.addObserver(lifecycleBoundObserver)后,即可在lifecycle状态(state)变化时,触发lifecycleBoundObserver.onStateChanged,根据lifecycle当前状态,决定observer是否活跃(active), 从而决定是否要接收livedata中的数据更新。
lifeBoundObserver.shouldBeActive方法,定义了lifeCycle至少处于STARTED以及之后状态,observer才是active的。
状态从inactive变成active时,lifeBoundObserver会调用 liveData.dispatchingValue(this)
方法,让liveData把mData中的告知自己,从而通知其包含的observer。
例如,一个在background的activity回到foreground时,其相关的observer也从inactive变成active,也就是onStart后,马上会收到当前livedata中的值。
需要知道的是,lifeCycle不仅会在state真正改变时通知lifecycleBoundObserver。调用 lifecycle.addObserver(lifecycleBoundObserver)
后,它会把达到当前状态前经历的所有LifeCycle.Event都发送给lifecycleBoundObserver。
例如,当前lifeCycle状态为RESUMED, lifecycle.addObserver(lifecycleBoundObserver)
后,
lifeBoundObserver.onStateChanged 马上连续收到ON_CREATE,ON_START,ON_RESUME三个事件。可以想象成“补收”之前的事件。在这种情况下,因为onStateChanged在收到ON_CREATE时,lifecycle已经处于RESUME状态,再STARTED之后,所以observer已是活跃的。所以比如在activity onResume后,onPause前,让一个obsever开始观察livedata,那么这个observer的onChange方法会立马被调用(如果mData已经设置过的情况)。
所以,observer并不只是再数据变化时收到“推送”,也可在lifecycle状态变化时收到通知。这也是上面提到,不能直接作为事件线使用的另一个原因。
LiveData.observe调用时机
在activity中,一般在onCreate回调中执行 liveData.observe(this, Observer<T> { ... })
。lifecycleOwner即activity本身。在activity由于销毁时,比如configuration change,livedata会把observer移除,也就不会有memory leak。
由于livedata的observer本身就会在STARTED之后开始观察,无需把绑定observer放在onStart/onResume中。
此外,虽然observe方法不会重复添加同一个obsever对象,如果在onStart/onResume中使用新建了匿名observer对象的方式绑定, liveData.observe(this, Observer<T> { ... })
,仍然会造成错误地重复绑定多个观察者。
在fragment中,需注意lifecycleOwner应该使用viewLifecycleOwner,而不是fragment本身(Android Studio已经会提醒)。fragment的lifecycle比较复杂。有些情况下,onDestroy不会调用到,即到达不了DESTROYED状态,造成observer没有被移除,绑定重复的observer。举个例子,一个非retained fragment实例,在configuration change时,将经历onPause>onStop>onDestroyView>onDetach>onAttach>..onStart>onResume, 其lifecycle不会经历destroyed状态,不管将 liveData.observe(this, Observer<T> { ... })
方法写在onAttach/onCreate/onCreateView/onViewCreated/onActivityCreated哪个回调中,都不可避免建立了重复的observer。使用viewLifecycleOwner,onDestroyView时,observer就会被移除掉。由于viewLifeCycleOwner必须在onCreateView之后才能获取,onCreateView/onViewCreated/onActivityCreated时调用 liveData.observe(viewLifeCycleOwner, Observer<T>{})
即可。选三个回调中任意一个差别不大,一般写在onActivityCreated里。
LiveData基础的使用介绍到这里,下一篇预告:LiveData基础使用方式+工作员(下篇) MediatorLiveData & Transforms
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 微软轻量级系统监控工具sysmon原理与实现完全分析(上篇)
- 威胁情报相关标准简介 (上篇)
- Tensorflow Rust实战上篇
- 【前端面试分享】- 寒冬求职上篇
- 认证授权方案之授权揭秘 (上篇)
- 流式处理框架storm浅析(上篇)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。