前言
以前排查过一个版本beta期间的崩溃跟内存泄漏问题,数据的同事从埋点发现商品列表筛选的后续流程是中断的,性能监控显示用户每次进入该页,内存占用都会增加,而且商品状态变更后带来NPE崩溃。
起因是我们在商品列表页面中使用了LiveData,但查看代码提交记录跟需求才知道,当时在商品列表页面中,我们使用observeForever来监听筛选条件的变化,但由于忘记在Fragment的onDestroy中移除观察者,导致观察者持有了 Fragment 的引用无法释放,最终在高频访问场景下引发了OOM。
同时旧版使用observe监听商品状态变化,后来为了在后台也能处理商品状态,被修改成了observeForever。这导致在Fragment已销毁的情况下,观察者仍在处理数据更新,访问了已经为null的View对象,引发NPE崩溃.
但今天不是为了讨论为什么当时项目里为什么这么写,而是通过走读源码知道内部的实现,能更清楚认识LiveData的使用.
- LiveData的通知条件,怎样才满足通知的要求?
- LiveData的观察到底需不需要解除观察,为什么?
- LiveData凭什么在Activity或者Fragment可见时能收到回调,怎样算可见?
- LiveData的postValue背压实现是怎样?
从LiveData开始
抽象类
代码注释我稍微翻译一下:
LiveData作为一个数据持有类,能用于观察给定的LifeCycle.这代表着一个Observer能够被添加到LifeCycleOwner,并只在该LifeCycleOwner处于active状态时,才被通知关于所包装数据的修改.LifeCycle.State.STARTED,LifeCycle.State.RESUMED都算是active状态.通过observeForever方法添加的观察者被视为始终active的观察者,始终通知有关修改的通知.对于这种类型添加的需要手动调用removeObserver移除
一个被添加到LifeCycle的Observer将会被自动移除,如果这个LifeCycle执行到了LifeCycle.State.DESTROYED状态.
此外,livedata具有onactive和onInactive方法,可以在active的观察者数量在0到1之间变化时获得通知。这让Livedata在没有任何active状态的观察者时释放任何重型资源。
该类旨在保存ViewModel的单个数据字段,但也可以用于以脱钩的方式共享应用程序中不同模块之间的数据。
好的,注释都说了"observeForever要手动处理,被添加到LifeCycle的Observer将会被自动移除"
熟悉我风格的都知道,就是头铁.注释里说的尽可能会从源码证明知道这些说法的来源.来,过一遍!
Observer
接口,提供一个简单的能接受LiveData的回调.确实也没什么内容
observe和observeForever:注册观察
LiveData支持了两种观察方法,分别是observe和observeForever方法
从截图内能提取大概流程:
- 检查是否在主线程
- (生命周期检测,observe需要)
- 创建包裹对象ObserverWrapper
- 以Observer为key,查询是否已经存在包裹对象,且是否已注册别的生命周期
- 未注册则保存,并执行各自观察后续补充一个启动生命周期的监听,一个手动触发activeStateChanged方法,传true
mObservers字段的实现采用的是SafeIterableMap,说明,也不是一个Map,只是Iterable接口的又一实现.同时该类注释里声明,这不是一个线程安全的迭代器.那为了确保线程安全,第一步检测线程,将容器操作确保在主线程执行也就合理了.
但mObservers字段里并不是直接对Observer进行收集管理,而是采用创建包裹对象的方式管理,这里打住,先看同observe对应的remove方法.
removeObserver:移除注册
Map中找到指定包裹对象移除掉并执行对象的回收方法,包括:
- 包裹对象ObserverWrapper的
detachObserver - ObserverWrapper的
activeStateChanged方法,但传false
没懂,这里还是想对ObserverWrapper的类做个拆解.
ObserverWrapper
抽象类,使用激活状态mActive、版本号mLastVersion完成标记和管理,从observe相关方法传入的Observer在这里被持有.并提供了抽象方法,同时扩展了两个类.
子类
-
AlwaysActiveObserver:对shouldBeActive方法重写,永远返回true.
-
LifecycleBoundObserver:对shouldBeActive方法重写外,还对其他几个方法都进行重写,除此之外,该类还作为LifecycleEventObserver接口实现类,所以多了个onStateChanged方法的实现.
还是不懂,我们结合方法的调用来看.
activeStateChanged:切换包裹类激活状态
从observeForever跟removeObserver方法里找到activeStateChanged方法的调用
查看方法内容包括如下步骤:
- active状态去重保存新状态
- 检测是否触发LiveData#changeActiveCounter活跃回调
- 如果true则尝试让LiveData#dispatchingValue走分发流程,将数据分发给Observer;传false则完成流程.
changeActiveCounter:active数量是否为空通知
通过检查上一个活跃数和当前活跃数变化确定onactive和onInactive调用.这里采用轮询的方式不停检查回调.
dispatchingValue:分发数据给Observer
核心在中间部分
步骤和循环的原因:
- 如果initiator不为null,只分发给initiator
- 否则遍历所有observer,调用considerNotify
- 分发过程中如果有新的分发请求,会重新循环
观察拦截字段内容也是进入方法后的标记拦截
considerNotify:数据变化考虑通知外部
变化触发检查:
- 包裹对象当前活跃状态
mActive==true - 再次确认包裹对象是否应该激活(shouldBeActive),否则切换为非激活
- 如果包裹对象已经收到最新数据(
mLastVersion >= mVersion),不再通知 - 更新
mLastVersion,调用持有的Observer的onChanged方法
到这里总算知道,前面ObserverWrapper的实现类对于shouldBeActive的重写意义.回到AlwaysActiveObserver类,这里永远为true,说明在LiveData执行considerNotify方法时,永远不会因为ObserverWrapper的活跃状态导致通知流程被拦截.
那LifecycleBoundObserver呢?
判断当前LiveCycleOwner的生命周期是否至少达到LiveCycle.State.STARTED.那怎样达到LiveCycle.State.STARTED?
以Activity为例子,onStart被调用之后,在onPause之前,这个阶段是可以的.那onResume方法被调用之后呢,都进入了LiveCycle.State.RESUMED了肯定算啊.这些情况下就满足,都视为active.那LiveCycle.State.DESTROYED呢!肯定不是啊!
到这里,我们就搞清楚了,active的识别标准和原因:
- observe绑定的Observer只有在生命周期阶段至少达到
LiveCycle.State.STARTED才算 - observeForever绑定的永远是active
这就解释了为什么LiveData在observe执行后,触发Observer时一定得是处于可见状态
同理,在LiveData的removeObserver方法内,移除同时还执行了ObserverWrapper#detachObserver方法,只有LifecycleBoundObserver做了实现,干的操作就是移除对生命周期方法的监听.
这里也能得到结论:
- observe方法调用时开始生命周期监听,在removeObserver时移除生命周期监听.
- observeForever方法不需要监听生命周期,removeObserver也不用移除监听.
但不是说,observe方法在生命周期销毁的时候会自动移除吗?
对啊,LifecycleBoundObserver实现了LifecycleEventObserver接口,在onStateChanged内做了处理
- 状态变化时检测active状态触发activeStateChanged,理想是true,用于触发后续considerNotify里让Observer#onChanged回调的内容
- 如果是生命周期销毁就执行removeObserver完成监听的闭环逻辑
postValue和setValue
区别在于
- 执行线程上:前者不关注所在线程;后者做主线程检测处理
- 执行时机上:前者通过 Handler 切到主线程,即异步执行;后者立即执行数据更新,同步执行
- 连续调用处理:前者在第一个Runnable执行之前的这段时间内,连续调用时即使获取到锁了,是多次修改
mPendingData值,Runnable触发执行时,取的就是最新的mPendingData;后者连续调用时保证所有值都会被分发
疑问
怎么实现的只保证最后一次提交的值?
- postValue内部尝试获取
mDataLock的锁,等待拿到锁以后,postTask标识才能知道当前mPendingData是否为无效值,如果mPendingData有效,则结果为false,不提交新Runable. - 在Runable被调用之前,多次postValue修改的是
mPendingData的值. - Runable被触发时,内部尝试获取
mDataLock的锁,mPendingData的最新值给newValue未处理完之前,不会释放锁(也就是说如果此时还有postValue,就是要等待锁的过程了).处理完确定newValue的值以后,释放mDataLock锁,开始进入setValue流程. - 这时由于后续的postValue的执行拿到锁,由于
mPendingData已经是无效的,所以记录参数值到mPendingData,后提交新的Runnable,这得视为新的一批独立调用.
为什么当绑定的 LifecycleOwner(比如 Activity 或 Fragment)的生命周期状态至少为 STARTED时,observer才会被认为是active,才会触发observer的onChanged方法
调用observe会创建LifecycleBoundObserver对象监听LifecycleOwner的生命周期.
onStateChanged内判断shouldBeActive时,LifecycleBoundObserver指定了mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);即当LifecycleOwner 的状态是 STARTED 或 RESUMED 时,ObserverWrapper才会被认为是 active;如果LifecycleOwner 处于 CREATED、INITIALIZED 或 DESTROYED,Observer不会收到数据变化的通知.