这是我参与更文挑战的第19天,活动详情查看: 更文挑战
源码分析
生命周期注册者 (LifecycleRegistry)
addObserver
@Override
public void addObserver(@NonNull LifecycleObserver observer) {
//todo 这段注释要再理解下
// 上文在dispatchEvent前后进行的pushParentState和popParentState就是为了解决这个问题:
// 如果在onStart中注销了observer又重新注册,这时重新注册的observer初始的状态为INITIALIZED;
// 而如果想执行ON_START的对应回调,需要newObserver处于CREATED状态(之前的状态因为removeObserver,不存在于mObserverMap中)
// mParentStates的作用就是为newObserver提供了CREATED这个状态
// 如果不是 DESTROYED,则从 INITIALIZED 开始分发
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
// ObserverWithState 用于分发事件给 observer
// ObserverWithState 里会做 Observer 的转换
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
// 放入 map 中
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
if (previous != null) {
return;
}
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
// it is null we should be destroyed. Fallback quickly
return;
}
// isReentrance 表示是否在分发事件时新添加了 observer
// 举个例子:在 observer 在 onStart() 中又调用了 addObserver()
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
// 引入 parentState, targetState 等都是为了保证列表中后加的 Observer 的状态不能大于前面的, 这样做之后,如果列表第一个和最后一个的状态和 LifecycleRegistry.mState 相等时,就说明状态同步完成了。
// 计算需要分发的状态
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
// 将事件逐步分发到 targetState
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
// 如果 statefulObserver.state 小于 targetState
pushParentState(statefulObserver.mState);
// 如果 state 为 STARTED,则 upEvent(state) 则为 ON_RESUME
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
//addObserver()主要考虑了 Reentrance 的情况,即在observer的事件分发中,又添加了新的 observer的情况。
if (!isReentrance) {
// we do sync only on the top level.
// 当前为重入,则不进行同步
sync();
}
mAddingObserverCounter--;
}
复制代码
private State calculateTargetState(LifecycleObserver observer) {
// 获取上一个添加的 observer
Entry<LifecycleObserver, ObserverWithState> previous = mObserverMap.ceil(observer);
State siblingState = previous != null ? previous.getValue().mState : null;
// mParentStates 是个 List,它的添加和删除分别由 pushParentState() 和 popParentState(),它们是成对出现的,在 dispatchEvent 的前后
// 在这种 case 下,会存在 parentState:在 dispatchEvent 时,又调用了 addObserver(),即上面说的 isReentrance
State parentState = !mParentStates.isEmpty() ? mParentStates.get(mParentStates.size() - 1)
: null;
// 这里的计算是获取更合适的状态
// 考虑以下这种 case:某个 observer 在 onStart() 中再调用 addObserver,那这个 observer 理应使用 STARTED 状态分发,而当前状态即 mState 可能是 RESUMED,再在 sync() 中进行同步
return min(min(mState, siblingState), parentState);
}
复制代码
addObserver
并不是简单的将 Observer 加入 map 中, 首先它会做上文提到的 Observer 转换。 其次需要考虑“重入问题”。 所谓的“重入问题”,就是 addObserver
会触发 Observer 生命周期函数的调用,而 Observer 在生命周期函数中又调用了 addObserver
等方法。 因此, LifecycleRegistry
用变量 mAddingObserverCounter
和 mHandlingEvent
来判断是否处于重入状态。
其他 LifecycleOnwer
LifecycleService
ServiceLifecycleDispatcher 将事件派发重新推到主线程消息队列,用于保证确保回调在 Service 生命周期回调后再调用。
ProcessLifecycleOwner
用于监听整个应用的前后台切换。也是利用 ActivityLifecycleCallback 监听每个 Activity 的生命周期,如果 onStop 事件后,没有监听到任意的 onStart 事件,那么 ProcessLifecycleOwner 就会认为整个应用切换到后台,同时留下一个标志。如果监听到 onStart 事件,同时检查有标志那么就会认为应用回到前台。
linxiaotao.github.io/2019/01/14/…
【奇技淫巧】使用 ProcessLifecycle 优雅地监听应用前后台切换
todo
其他
todo 下面这段可以放到其他文章去
能感知生命周期的组件的最佳实践
- 尽量让您的 UI 控制器(activity 和 fragment)别什么都自己扛着。它们不应试图获取自身的数据,而是应该通过
ViewModel
,并观察一个LiveData
对象来在视图上针对变动做出反应。 - 尝试编写数据驱动的 UI,并让您的 UI 控制器负责在数据变化的时候更新视图、或者把用户的行为通知给
ViewModel
。 - 将您的数据逻辑放入
ViewModel
类。ViewModel
应当充作您的 UI 控制器和应用其余部分的连接。请小心注意:获取数据(比如从网络下载)并不是ViewModel
的责任,恰恰相反,ViewModel
应当调用合适的组件去获取数据,再将结果提供给 UI 控制器。 - 使用数据绑定来在您的视图和 UI 控制器之间维持整洁的接口。这有利于让您的视图更具陈述性,而且让您在 UI 控制器中只需添加最少的更新逻辑。如果您更愿意使用 Java 语言来进行数据绑定,使用诸如 Butter Knife 的库可以避免八股代码而获得更好的抽象。
- 如果您的 UI 很复杂,请考虑创建一个 presenter 类来处理 UI 修改。这可能会比较累人,但可以让您的 UI 组件更易于测试。
- 避免在您的
ViewModel
中直接引用View
或Activity
等 context。如果ViewModel
比其 activity 还活得长久(比如配置变更),您的 activity 就内存泄漏了,很难被 GC 得当回收。
能感知生命周期的组件的用例
能感知生命周期的组件能让许多用例中的生命周期管理变得简单。以下是几个例子:
- 切换地理位置更新的精度。当您的应用可见时,启动高精度的地理位置更新;当您的应用进入后台时,切换成低精度的更新。
LiveData
就是一个能感知生命周期的组件,让您的应用在用户移动的时候自动更新 UI。 - 开始和停止视频缓冲。尽快开始视频缓冲,但把回放延迟到应用完全启动之后。您还可以使用这样的组件来在应用被销毁时停止视频缓冲。
- 开始和停止网络连接。当应用在前台时,开启使用网络连接的流媒体;当应用在后台时自动暂停。
- 暂停和恢复动画 drawable。当应用进入后台时,处理动画 drawable 的暂停;当应用回到前台时,恢复动画。
处理 onStop 事件
当 Lifecycle
属于一个 AppCompatActivity
或 Fragment
,Lifecycle
的状态会变为 CREATED
,而 ON_STOP
事件会在 AppCompatActivity
或 Fragment
的 onSaveInstanceState()
被调用时分发。
当一个 Fragment
或 AppCompatActivity
的状态在 onSaveInstanceState()
中保存时,直到 ON_START
触发之前,其 UI 被认为是不可变动的。试图在状态保存后更改 UI 可能会导致您的应用产生不一致的导航状态,这就是为什么当应用在状态保存之后试图执行 FragmentTransaction
时,FragmentManager
会抛出异常。欲了解更多信息,请参阅 commit()
。
LiveData
提供了对这种边缘情况的开箱即用的支持:如果观察者所联系的 Lifecycle
连 STARTED
都不是的话,它就不会调用该观察者。其内部实现是先调用 isAtLeast()
来决定是否要触发其观察者。
悲剧的是,AppCompatActivity
的 onStop()
是在 onSaveInstanceState()
之后调用的,这导致了一段 UI 不允许改动、但 Lifecycle
还没进入 CREATED
状态的间隙。
为了防止这样的悲剧,beta2
及更低版本的 Lifecycle
将状态标记为 CREATED
但并不分发事件,这样一来任何检查当前状态的代码都会得到真正的值,尽管直到系统调用 onStop()
之前事件都没被分发。
二次悲剧的是,这种解决方案有两个主要的问题:
- 在 23 及更低的 API 版本上,一个 activity 即使被另一个 activity 部分遮挡,Android 系统实际上也会保存其状态。换言之,Android 系统调用了
onSaveInstanceState()
,但并不一定调用onStop()
。这导致了一个可能很长的间隙,期间观察者仍然认为生命周期是活动的,但其实 UI 状态不能改动。 - 任何想要把类似行为暴露给
LiveData
的类都需要实现beta2
或更低版本的Lifecycle
所提供的变通方案。
注意:为了让上述流程更简单、向后兼容性更好,从 1.0.0-rc1 版本开始,Lifecycle 对象会被标记为 CREATED,而 ON_STOP 会在 onSaveInstanceState() 被调用时分发、毋须等待 onStop 方法被调用。这应该不太会影响您的代码,但您仍然需要了解,因为它和 26 及更高版本的 API 中 Activity 方法的调用顺序并不一致。
以上是来自官方文档的翻译,翻译地址见参考一。
参考
Android官方架构组件Lifecycle:生命周期组件详解&原理分析
Android arch components 源码分析(2)—— Lifecycle