⭐️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 [BaguTree Pro] 知识星球提问。
Android Jetpack 开发套件是 Google 推出的 Android 应用开发编程范式,为开发者提供了解决应用开发场景中通用的模式化问题的最佳实践,让开发者可将时间精力集中于真正重要的业务编码工作上。
这篇文章是 Android Jetpack 系列文章的第 1 篇文章,完整目录可以移步至文章末尾~
前言
- 生命周期是 Activity 的核心特性之一,也是 Android 视图开发无法规避的重要问题。 为了更加健壮地处理生命周期问题,Google 的解决方案是将生命周期定义为一套标准的行为模式,即 Lifecycle 框架。 这种方式不仅简化了在 Activity / Fragment 等生命周期宿主中分发生命周期事件的复杂度,还提供了自定义生命周期宿主的标准模板;
- Lifecycle 是多个 Jetpack 组件的基础,例如我们熟悉的 LiveData 就是以 Lifecycle 为基础实现的生命周期感知型数据容器,因此我们选择将 Lifecycle 放在 Jetpack 系列的第一篇。
- 认识 Lifecycle
1.1 为什么要使用 Lifecycle?
Lifecycle 的主要作用是简化实现生命周期感知型组件的复杂度。 在传统的方式中,需要手动从外部宿主(如 Activity、Fragment 或自定义宿主)中将生命周期事件分发到功能组件内部,这势必会造成宿主代码复杂度增加。例如:
MyActivity.kt
1 | jsx复制代码// Activity 宿主 |
而使用 Lifecycle 组件后,能够将分发宿主生命周期事件的方法迁移到功能组件内部,宿主不再需要直接参与调整功能组件的生命周期。例如:
MyActivity.kt
1 | jsx复制代码// Activity 宿主 |
MyWorker.kt
1 | jsx复制代码class MyWorker : LifecycleEventObserver { |
1.2 Lifecycle 的设计思路
Lifecycle 整体上采用了观察者模式,核心的 API 是 LifecycleObserver 和 LifecycleOwner:
- LifecycleObserver: 观察者 API;
- LifecycleOwner: 被观察者 API,生命周期宿主需要实现该接口,并将生命周期状态分发 Lifecycle,从而间接分发给被观察者;
- Lifecycle: 定义了生命周期的标准行为模式,属于 Lifecycle 框架的核心类,另外框架还提供了一个默认实现 LifecycleRegistry。
LifecycleObserver.java
1 | jsx复制代码public interface LifecycleObserver { |
LifecycleOwner.java
1 | jsx复制代码public interface LifecycleOwner { |
1.3 Lifecycle 的使用方法
- 添加依赖: 在 build.gradle 中添加 Lifecycle 依赖,需要注意区分过时的方式:
模块 build.gradle
1 | jsx复制代码// 过时方式(lifecycle-extensions 不再维护) |
- 注册观察者: Lifecycle 通过 addObserver(LifecycleObserver) 接口注册观察者,支持通过注解或非注解的方式注册观察者,共分为 3 种:
+ ~~**1、LifecycleObserver(注解方式 ,不推荐):**~~ 在这个场景使用注解处理有种杀鸡用牛刀的嫌疑,并没有比其他两种方式有优势。注解方式存在注解处理过程,并且如果在依赖时遗漏注解处理器的话,还会退化为使用反射回调,因此不推荐使用。
1 | jsx复制代码lifecycle.addObserver(object : LifecycleObserver { |
+ **2、LifecycleEventObserver(非注解方式,推荐)**
1 | jsx复制代码lifecycle.addObserver(object : LifecycleEventObserver { |
+ **3、DefaultLifecycleObserver(非注解方式,推荐)**
1 | kotlin复制代码// DefaultLifecycleObserver 是 FullLifecycleObserver 接口的空实现 |
注意: Lifecycle 内部会禁止一个观察者注册到多个宿主上。这很好理解,要是绑定了多个宿主的话,Lifecycle 就不知道以哪个宿主的生命周期为准了。
1.4 预定义的宿主
目前,Android 预定义的 Lifecycle 宿主有 3 个:Activity、Fragment 和应用进程级别的宿主 ProcessLifecycleOwner:
- 1、Activity(具体实现在 androidx.activity.ComponentActivity)
- 2、Fragment
- 3、ProcessLifecycleOwner
前两个宿主大家都很熟悉了,第 3 个宿主 ProcessLifecycleOwner 则提供整个应用进程级别 Activity 的生命周期,能够支持非毫秒级别精度监听应用前后台切换的场景。
- Lifecycle.Event.ON_CREATE: 在应用进程启动时分发,只会分发一次;
- Lifecycle.Event.ON_START:在应用进程进入前台(STARTED)时分发,可能分发多次;
- Lifecycle.Event.ON_RESUME:在应用进程进入前台(RESUMED)时分发,可能分发多次;
- Lifecycle.Event.ON_PAUSE:在应用退出前台(PAUSED)时分发,可能分发多次;
- Lifecycle.Event.ON_STOP:在应用退出前台(STOPPED)时分发,可能分发多次;
- Lifecycle.EVENT.ON_DESTROY:注意,不会被分发。
使用示例
1 | jsx复制代码ProcessLifecycleOwner.get().lifecycle.addObserver(object: LifecycleEventObserver{ |
1.5 自定义宿主
观察者必须绑定到宿主 LifecycleOwner 上,你可以使用系统预定义的宿主,或根据需要自定义宿主。主要步骤是实现 LifecycleOwner 并在内部将生命周期事件分发给调度器 LifecycleRegistry。模板如下:
LifecycleOwner.java
1 | jsx复制代码public interface LifecycleOwner { |
MyLifecycleOwner.kt
1 | jsx复制代码/** |
- Lifecycle 实现原理分析
2.1 注册观察者的执行过程
Lifecycle#addObserver() 最终会分发到调度器 LifecycleRegistry 中,其中会将观察者和观察者持有的状态包装为一个节点,并且在注册时将观察者状态同步推进到与宿主相同的状态中。
LifecycleRegistry.java
1 | jsx复制代码private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap = new FastSafeIterableMap<>(); |
2.2 Lifecycle 如何适配不同类型的观察者
为了适配上面提到的不同类型的观察者,LifecycleRegistry 还为它们提供了一个适配层:非注解的方式会包装为一个 LifecycleEventObserver 的适配器对象,对于注解的方式,如果项目中引入了 annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
,会在编译时生成工具类 MyObserver_LifecycleAdapter
,否则会使用反射回调注解方法。
LifecycleRegistry.java
1 | jsx复制代码// ObserverWithState:观察者及其观察状态 |
Lifecycling.java
1 | jsx复制代码@NonNull |
FullLifecycleObserverAdapter.java
1 | jsx复制代码class FullLifecycleObserverAdapter implements LifecycleEventObserver { |
2.3 Lifecycle 如何感知 Activity 生命周期
宿主的生命周期事件需要分发到调度器 LifecycleRegistry 中,在高版本有直接观察 Activity 生命周期的 API,而在低版本使用无界面的 Fragment 间接观察 Activity 的生命周期。
androidx.activity.ComponentActivity.java
1 | jsx复制代码public class ComponentActivity extends androidx.core.app.ComponentActivity implements LifecycleOwner ...{ |
ReportFragment.java
1 | jsx复制代码// 空白 Fragment |
2.4 Lifecycle 分发生命周期事件的过程
当宿主的生命周期发生变化时,会分发到 LifecycleRegistry#handleLifecycleEvent(Lifecycle.Event)
,将观察者的状态回调到最新的状态上。
LifecycleRegistry.java
1 | jsx复制代码private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =new FastSafeIterableMap<>(); |
- Lifecycle 实践案例
3.1 使用 Lifecycle 解决 Dialog 内存泄漏
在 Activity 结束时,如果 Activity 上还存在未关闭的 Dialog,则会导致内存泄漏:
1 | jsx复制代码WindowLeaked: Activtiy MainActivity has leaked window DecorView@dfxxxx[MainActivity] thas was originally added here |
解决方法:
- 方法 1:在 Activity#onDestroy() 中手动调用 Dialog#dismiss();
- 方法 2:替换为 DialogFragment,内部会在 Fragment#onDestroyView() 时关闭 Dialog;
- 方法 3:自定义 BaseDialog,使用 Lifecycle 监听宿主 DESTROYED 生命周期关闭 Dialog:
BaseDialog.kt
1 | jsx复制代码class BaseDialog(context: Context) : Dialog(context), LifecycleEventObserver { |
3.2 生命周期感知型协程
Lifecycle 也加强了对 Kotlin 协程的支持 LifecycleCoroutineScope,我们可以构造出与生命周期相关联的协程作用域,主要支持 2 个特性:
- 1、在宿主消亡(DESTROYED)时,自动取消协程;
- 2、在宿主离开指定生命周期状态时挂起,在宿主重新进入指定生命周期状态时恢复协程(例如 launchWhenResumed)。
使用示例
1 | jsx复制代码// 示例 1 |
1、自动取消协程实现原理分析: 核心在于 LifecycleCoroutineScopeImpl 中,内部在初始化时会注册一个观察者到宿主生命周期上,并在宿主进入 DESTROYED 时取消(cancel)协程。
LifecycleOwner.kt
1 | jsx复制代码// 基于 LifecycleOwner 的扩展函数 |
Lifecycle.kt
1 | jsx复制代码public val Lifecycle.coroutineScope: LifecycleCoroutineScope |
2、关联指定生命周期实现原理分析: 实现原理也是类似的,launchWhenResumed() 内部在 LifecycleContro 中注册观察者,最终通过协程调度器 PausingDispatcher
挂起(pause)或恢复(resume)协程。
PausingDispatcher.kt
1 | jsx复制代码public suspend fun <T> LifecycleOwner.whenResumed(block: suspend CoroutineScope.() -> T): T = |
LifecycleController.kt
1 | jsx复制代码@MainThread |
3.3 安全地观察 Flow 数据流
我们知道,Kotlin Flow 不具备生命周期感知的能力(当然了,Flow 是 Kotlin 生态的组件,不是仅针对 Android 生态的组件),那么 Flow 观察者如何保证在安全的生命周期订阅数据呢?
- 方法 1:使用生命周期感知型协程(不推荐)
- 方法 2:使用 Flow#flowWithLifecycle() API(推荐)
具体分析在 4、Flow:LiveData 的替代方案 这篇文章里都讲过,这里不重复。
- 总结
到这里,Jetpack 中最基础的 Lifecycle 组件就讲完了,下几篇文章我们将讨论基于 Lifecycle 实现的其他 Jetpack 组件,你知道是什么吗?关注我,带你了解更多。
参考资料
- 使用生命周期感知型组件处理生命周期 —— 官方文档
- Lifecycle,看完这次就真的懂了 —— g小志 著
- 使用 ProcessLifecycle 优雅地监听应用前后台切换 —— Flywith24 著
推荐阅读
Android Jetpack 系列文章目录如下(2023/07/08 更新):
- #1 Lifecycle:生命周期感知型组件的基础
- #2 为什么 LiveData 会重放数据,怎么解决?
- #3 为什么 Activity 都重建了 ViewModel 还存在?
- #4 有小伙伴说看不懂 LiveData、Flow、Channel,跟我走
- #5 Android UI 架构演进:从 MVC 到 MVP、MVVM、MVI
- #6 ViewBinding 与 Kotlin 委托双剑合璧
- #7 AndroidX Fragment 核心原理分析
- #8 OnBackPressedDispatcher:Jetpack 处理回退事件的新姿势
- #9 食之无味!App Startup 可能比你想象中要简单
- #10 从 Dagger2 到 Hilt 玩转依赖注入(一)
⭐️ 永远相信美好的事情即将发生,欢迎加入小彭的 Android 交流社群~
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。
本文转载自: 掘金