Lifecycle(生命周期) 在任何 GUI 编程中都是基石般的存在,Android 也不例外。
作为用户,在页面跳转,旋转屏幕,查看通知,切换应用等日常操作中,都期望获得流畅连贯的使用体验。在这背后,就需要我们开发者在生命周期组件的不同阶段中进行相应的逻辑处理。这里的生命周期组件,可能是整个应用,也可能是单个页面。对应到 Android 中,Activity、Fragment,甚至 Service 都可以成为生命周期组件。
现在假设这样一个需求,你需要封装一个播放器的基础组件,供各个部门调用。最基本的流程就是打开 Activity,初始化资源并播放,退出 Activity,停止播放。
不假思索直接下笔,就很容易诞生下面这样的 “屎山代码” 。
class ShitAVPlayer { fun init() {} fun start() {} fun stop() {} } 复制代码
调用方需要在 Activity/Fragment 等生命周期组件的不同生命周期回调中,调用相应的方法。
class VideoActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) shitAVPlayer.init() shitAVPlayer.start() } override fun onStop() { super.onStop() shitAVPlayer.stop() } } 复制代码
功能实现上没有太大问题,但下面几个场景难免会给别人一种在屎山翻屎的感觉。
- 你的 ShitAVPlayer 作为基础组件提供给其他部门使用,当量级达到一定程度时,你没有办法保证每一个调用方都能合理且正确的处理生命周期,满地跑的内存泄漏一定少不了你的锅。
— 什么?你自己没调用,还赖我?
— 鬼知道我一个生命周期回调中要处理多少逻辑?你的 ShitAVPlayer 人如其名!
1.如果你的 ShitAVPlayer 需要在 onCreate()
中进行初始化,并提供了一个异步回调告知初始化状态,以判断是否可以开启。那么你可能无法保证执行回调时,生命周期组件是否已经 onStop()
,这又得依靠调用方自行处理。这就可能造成不符合预期的生命周期情况。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) shitAVPlayer.init { success -> // 是否还有必要开启 if (success) shitAVPlayer.start() } } 复制代码
- 如果产品给你加了这么一个需求,播放时按 Home 键回到桌面要暂停播放,返回时再恢复播放。你三下五除二给
ShitAVPlayer
加了onResume()
和onPause()
方法,然后花费大量时间和各个调用方同步。
— 老哥,有空在你的 Activity 的 onResume 和 onPause 回调加个方法呗,😜
— 加你个 xx !
本着是人就会犯错的原则,一个优秀基础组件的基本修养就是 能不麻烦别人的事情就尽量自己做 。
要想把 ShitAVPlayer
变成 ExcellentAVPlayer
,就得 赋予组件感知外部生命周期的能力。
基于观察者模式的 Lifecycle
梳理一下需求,我们需要让 ShitAVPlayer
能够 自动感知外部生命周期组件的生命周期事件或者回调。 这里存在三个角色:
- 生命周期组件,不妨叫做 LifecycleOwner 。Activity/Fragment/Service 等等任何具有生命周期的组件,都可以当作 LifecycleOwner 。
- 生命周期观察者,不妨叫做 LifecycleObserver 。ShitAVPlayer 这一类的需要感知生命周期的对象。
- 生命周期 本体。在面向对象的世界里,不可避免的需要一个 Lifecycle 类来表示生命周期。
很显然,这是一个典型的观察者模式,LifecycleObserver 观察 LifecycleOwner 的生命周期变化。
按照观察者模式的基本套路,需要给 LifecycleOwner 添加 addObserver
、removeObserver
等方法。但考虑到 Android 中的生命周期组件可能有 Activity、Fragment、Service 等等,直接让它们实现代码逻辑并不合适,不妨让 Lifecycle 来承担这部分功能,同时让 LifecycleOwner 持有 Lifecycle ,尽可能的符合开闭原则。
public interface LifecycleOwner { Lifecycle getLifecycle(); } 复制代码
观察者模式的核心逻辑都交给 Lifecycle 来处理 ,它需要提供的功能主要有这三个:
- 新增/删除观察者
- 接收外部生命周期组件传递过来的 生命周期事件
- 同步内部观察者的 生命周期状态
public class Lifecycle { // 保存观察者及其生命周期状态 private Map<LifecycleObserver,State> mObserverMap = .... // 新增观察者 public void addObserver(LifecycleObserver observer) {} // 移除观察者 public void removeObserver(LifecycleObserver observer) {} // 接收 LifecycleOwner 传递过来的生命周期事件 public void handleLifecycleEvent(Lifecycle.Event event) { // 根据生命周期事件,推导出应该处于的生命周期状态 moveToState(event.getTargetState()); } private void moveToState(State next) { // 同步观察者生命周期状态 sync(); } private void sync() { mObserverMap.forEach{ observer -> // 调用对应的生命周期回调 observer.onXXX() } } } 复制代码
对于观察者 LifecycleObserver 来说,可以提供一个默认接口,提供处理生命周期的能力。
interface FullLifecycleObserver extends LifecycleObserver { void onCreate(LifecycleOwner owner); void onStart(LifecycleOwner owner); void onResume(LifecycleOwner owner); void onPause(LifecycleOwner owner); void onStop(LifecycleOwner owner); void onDestroy(LifecycleOwner owner); } 复制代码
为了避免冗余代码,可以利用 java 8 的默认接口实现进行简化。
public interface DefaultLifecycleObserver extends FullLifecycleObserver { @Override default void onCreate(LifecycleOwner owner) {} @Override default void onStart(LifecycleOwner owner) {} @Override default void onResume(LifecycleOwner owner) {} @Override default void onPause(LifecycleOwner owner) {} @Override default void onStop(LifecycleOwner owner) {} @Override default void onDestroy(LifecycleOwner owner) {} } 复制代码
现在来优化 ShitAVPlayer
就很简单了,直接让他实现 DefaultLifecycleObserver 。
class ExcellentAVPlayer : DefaultLifecycleObserver { override fun onCreate(owner: LifecycleOwner) { super.onCreate(owner) init { success -> if (success && lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { start() } } } override fun onResume(owner: LifecycleOwner) { super.onResume(owner) resume() } override fun onPause(owner: LifecycleOwner) { super.onPause(owner) pause() } override fun onStop(owner: LifecycleOwner) { super.onStop(owner) stop() } private fun start() {} private fun stop() {} private fun pause() {} private fun resume() {} private fun init(action:(Boolean)->Unit){ } } 复制代码
对于调用方来说,只需要一句代码,一劳永逸的解决生命周期问题。
class VideoActivity : AppCompatActivity() { private val excellentAVPlayer by lazy { ExcellentAVPlayer() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // here lifecycle.addObserver(excellentAVPlayer) } } 复制代码
Event 和 State 的必要性
生命周期事件(Event) 和 生命周期状态(State) ,不知道上一节中,你有没有留意到这两个名词。
在基于观察者模式的生命周期通知机制中,为什么要引入 Event 和 State 呢?仅仅基于 Event 可以吗?
在现有的实现机制中,LifecycleOwner 在自身的生命周期回调中分发 Event,Lifecycle 接收到 Event 之后计算出对应的 State,然后将观察者队列中的所有 LifecycleObserver 同步到此 State 。最后依然是根据 Event 回调 LifecycleOvserver 的相应方法。那么,State 的作用是什么?
Event 是一个点,用于生命周期事件的通知。而 State 是一个块,表示生命周期的一个阶段,它可以帮助需要依赖生命周期组件的第三方组件快速获取当前所处的生命周期阶段,从而做出一些合理操作。就像 ExcellentAVPlayer 中的 init 回调,
init { success -> // 只有 STARTED 阶段才需要 start() if (success && lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { start() } } 复制代码
init() 是一个耗时过程,当初始化完成时,需要判断是否需要 start(),这时候就需要获取生命周期组件的此时的生米给你周期状态 State 。
Event 和 State 的对应关系也很好理解,我画了一张图。
上面代码中的 state.isAtLeast(Lifecycle.State.STARTED)
就是指 ON_START 之后到 ON_STOP 之前的阶段。
Event 和 State 通力合作,使得第三方组件不仅可以感知生命周期变化,还可以随时随地获取当前的生命周期状态,游刃有余的解决各种生命周期问题。
Lifecycle 解决的问题
上文中的伪代码和具体的源码实现有较大出入,本文旨在把握整体思想,不会过于注重源码的细枝末节。总结一下,观察者模式的 Lifecycle 到底解决了什么问题?
赋予组件感知外部生命周期的能力,将繁琐的样板代码解耦到组件内部,解救臃肿的生命周期代码。防止发生不符合预期的生命周期情况。
Lifecycle 作为 Jepack 甚至整个操作系统的核心基础,还是相当有必要读一读 the fucking souce code 。下一篇见!