前言:为什么 Lifecycle 是现代 Android 架构的「基石」
在 Android 的整个演进过程中,生命周期管理一直是开发者绕不开的难题。传统做法要求我们在 Activity / Fragment 的生命周期方法(onCreate / onStart / onStop / onDestroy 等)里手动:
- 启动/停止各种组件(定位、网络、传感器、播放器…)
- 注册/反注册监听器(广播、回调、观察者…)
- 创建/释放资源(线程池、协程、数据库连接…)
随着业务复杂度上升,这套模式会迅速恶化:
- 控制器巨无霸:大量业务代码塞进
Activity/Fragment - 资源泄漏 & 崩溃:稍有疏忽就忘记释放资源或在已销毁视图上更新 UI
- 状态处理混乱:前后台切换、旋转重建、进程重启等场景极易出 bug
Jetpack Lifecycle 的目标,就是通过「可观察的生命周期 + 明确的状态机」,让业务组件自动感知宿主生命周期,从而:
- 把「在什么时候执行什么」的逻辑,从 UI 控制器中剥离出去
- 与协程、Flow、ViewModel 等现代组件无缝集成
- 让内存安全、UI 安全成为默认行为,而不是靠经验与习惯
第一部分:Lifecycle 的核心模型——State + Event + Observer
1.1 从「回调地狱」到「生命周期感知组件」
传统写法(反例): 在 onStart/onStop 里手动启停组件,onCreate 里初始化上述组件(代码省略),极易漏写某一处的释放。
class TraditionalActivity : AppCompatActivity() {
private lateinit var locationTracker: LocationTracker
private lateinit var dataSync: DataSyncer
// 省略:onCreate 中初始化上述组件
override fun onStart() {
super.onStart()
locationTracker.start()
dataSync.beginSync()
}
override fun onStop() {
super.onStop()
locationTracker.stop()
// dataSync.endSync() // ❌ 易遗漏 → 泄漏
}
}
Lifecycle 思路:
把「什么时候开始/结束」交给生命周期,让组件自己对生命周期作出反应。
class ModernActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(LocationTrackerObserver())
lifecycle.addObserver(DataSyncObserver())
}
// 不再需要覆写 onStart / onStop 专门编排这些组件
}
传统 vs Lifecycle 对比:
| 维度 | 传统方式 | Lifecycle 方式 |
|---|---|---|
| 代码组织 | 业务逻辑散落在生命周期里 | 逻辑封装在独立 Observer |
| 内存安全 | 易忘释放,易泄漏 | 在 onDestroy 等统一释放 |
| 可复用性 | 与具体 Activity 强耦合 | 任意 LifecycleOwner 可复用 |
Activity/Fragment只负责「提供生命周期」LifecycleObserver只关心「在某个状态我要干什么」- 结构上天然形成关注点分离:控制器变轻,组件可复用、可单测
1.2 三个核心角色与关系图
1)LifecycleOwner(拥有者)
- 谁有生命周期,谁就是
LifecycleOwner - 如:
ComponentActivity、Fragment、ProcessLifecycleOwner(应用前后台:ON_START 表示至少一个 Activity 可见、ON_STOP 表示全进后台)、自定义LifecycleOwner
2)Lifecycle(生命周期)
- 抽象类,内部维护当前状态(State)和事件(Event);唯一实现类是
LifecycleRegistry,负责状态机与事件分发。我们日常拿到的getLifecycle()返回的就是LifecycleRegistry实例。
3)LifecycleObserver(观察者)
- 任何想「跟随」
LifecycleOwner生命周期的组件 - 通过
LifecycleOwner.getLifecycle().addObserver(...)注册
关系:
LifecycleOwner持有一个LifecycleLifecycleObserver注册到Lifecycle- 当系统的生命周期回调经 ReportFragment(API < 29)或 ActivityLifecycleCallbacks(API ≥ 29)转成事件后,
Lifecycle更新自身状态,并通知所有Observer
可以概括成一句话:
「系统 → LifecycleOwner → Lifecycle → LifecycleObserver」
graph LR
Sys[Android 系统] --> Owner[LifecycleOwner]
Owner --> Lifecycle[LifecycleRegistry]
Lifecycle --> O1[Observer 1]
Lifecycle --> O2[Observer 2]
Lifecycle --> O3[Observer N]
1.3 State & Event:面试必讲的生命周期状态机
1. State 与 Event 是什么
-
State(状态):表示「此刻处在哪个阶段」,共 5 个。
INITIALIZED:对象已创建,尚未执行onCreateCREATED:已执行onCreate,可能在前台也可能在后台(例如onStop之后)STARTED:已可见,但不一定可交互(如被半透明 Dialog 盖住)RESUMED:完全可交互的前台(top resumed)DESTROYED:生命周期终点
-
Event(事件):表示「刚刚发生了哪一次生命周期回调」,是驱动状态变化的边,共 6 个,与
onCreate/onStart/onResume/onPause/onStop/onDestroy一一对应。
为什么 State 5 个、Event 6 个?
State 描述的是阶段(你在哪儿),多个回调可以落在同一阶段:例如 CREATED 既可能是刚执行完 onCreate,也可能是刚执行完 onStop(进后台)。所以用 5 个阶段就够表达「初始化 → 已创建 → 已可见 → 已获焦点 → 已销毁」;需要区分「怎么进到当前阶段」时,看 Event。Event 与 6 个回调一一对应,所以是 6 个。
2. 状态机怎么转:一张表 + 一张图
状态机的规则就是:用户/系统做了某件事 → 系统调某个生命周期方法 → 对应一个 Event → 当前 State 变为新值。下面这张表把「场景、回调、Event、新状态」一次性说清:
| 场景(你/系统干了啥) | 系统调的回调 | Event | 新状态 |
|---|---|---|---|
| 打开 Activity | onCreate() | ON_CREATE | CREATED |
| 从不可见变为可见 | onStart() | ON_START | STARTED |
| 完全显示、可点击 | onResume() | ON_RESUME | RESUMED |
| 被弹窗盖住 / 按 Home | onPause() | ON_PAUSE | STARTED |
| 完全不可见(切到别的 App) | onStop() | ON_STOP | CREATED |
| 关闭或 finish() | onDestroy() | ON_DESTROY | DESTROYED |
因此:按 Home 会先 onPause(RESUMED→STARTED),再 onStop(STARTED→CREATED);从后台返回则先 onStart 再 onResume。状态可前进也可回退。
stateDiagram-v2
[*] --> INITIALIZED
INITIALIZED --> CREATED : ON_CREATE
CREATED --> STARTED : ON_START
STARTED --> RESUMED : ON_RESUME
RESUMED --> STARTED : ON_PAUSE
STARTED --> CREATED : ON_STOP
CREATED --> DESTROYED : ON_DESTROY
DESTROYED --> [*]
3. 为什么用 State+Event,而且两个都要?
和「直接写 Activity 的 onStart/onStop」比:
直接写回调会把逻辑和具体页面绑死,想复用到别的 Activity 或 Fragment 只能复制粘贴,而且「当前到底在哪个阶段」没有统一表示,只能自己用变量记。
用 State+Event 后:Activity 和 Fragment 都共用同一套「当前状态 + 刚发生的事件」,组件不关心具体是哪个类、哪个回调名;可以随时用 lifecycle.currentState.isAtLeast(STARTED) 判断是否至少可见,不用自己维护标志位;lifecycleScope、repeatOnLifecycle 等也都基于这套状态机。
为什么不能只要一个?
- 只有 State:当前都是
CREATED时,看不出是「刚 onCreate」还是「刚 onStop」——前者通常做初始化,后者做收尾释放,逻辑不同。观察者必须知道是 ON_CREATE 还是 ON_STOP 才能写对,所以需要 Event 表示「怎么来的」。 - 只有 Event:没有「当前阶段」这一个变量,要回答「现在是否至少已经 STARTED」只能根据历史事件自己推,难写且易错。状态机也天然需要「处在哪个阶段」来做比较(例如「比 CREATED 更往前了吗?」),只有事件流就没有「阶段」这个概念。所以需要 State 表示「处在哪」,便于查询和比较。
小结:State 管「处在哪」、便于查询与比较;Event 管「怎么来的」、便于观察者按「发生了什么事」做不同反应。两者一起才能既方便查询阶段,又能在同一阶段下区分不同来源。
4. 事件分发时机(原理常考)
| 事件类型 | 分发时机 | 含义 |
|---|---|---|
| ON_CREATE / ON_START / ON_RESUME | 对应生命周期方法返回之后 | 已经进入新状态,观察者收到时当前就是新阶段,适合做「开始/恢复」逻辑 |
| ON_PAUSE / ON_STOP / ON_DESTROY | 对应生命周期方法调用之前 | 即将离开当前状态,观察者可提前释放资源,避免在方法执行完后再收尾产生竞态 |
「方法」指谁? 指 LifecycleOwner(如 Activity/Fragment)的那几个生命周期方法本身,不是指 super.onCreate() 的调用顺序。具体说:「返回之后」 = Activity 的 onCreate() / onStart() / onResume() 整段执行完、方法 return 之后,再给观察者发 ON_CREATE/ON_START/ON_RESUME;「调用之前」 = 在系统要调 Activity 的 onPause() / onStop() / onDestroy() 之前,先给观察者发 ON_PAUSE/ON_STOP/ON_DESTROY,观察者收尾完了,再执行 Activity 的对应方法。这样进入新状态时观察者「跟着一起进」,离开当前状态时观察者「先收尾、再让宿主方法跑」。
graph LR
A1[Activity onXxx 执行] --> A2[return]
A2 --> A3[再 dispatch 给观察者]
B1[先 dispatch 给观察者] --> B2[观察者收尾]
B2 --> B3[系统再调 Activity onXxx]
设计意图:进入新状态后再通知「可以做事」,离开前通知「该收尾了」。
5. 面试可说的三句话
- Jetpack Lifecycle 把生命周期抽象成一个有向状态机。
- 系统生命周期回调被转换成
Event,由LifecycleRegistry驱动State变化。 - 每次状态迁移,所有
Observer按一致顺序收到通知。
1.4 三种 Observer API:怎么选是加分点
1)DefaultLifecycleObserver(推荐)
- 接口方法直接是
onCreate/onStart...,类型安全,有 IDE 补全 - 不依赖反射,性能好
- 需要
lifecycle-common-java8依赖(基本是标配)
class LoggingObserver : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) { Log.d("L", "onCreate") }
override fun onStart(owner: LifecycleOwner) { Log.d("L", "onStart") }
override fun onDestroy(owner: LifecycleOwner) { Log.d("L", "onDestroy") }
// onResume / onStop 同理
}
2)LifecycleEventObserver(灵活)
- 所有事件统一从一个
onStateChanged进来 - 适合做一些「事件总线式」的桥接、调度
class EventObserver : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
when (event) {
Lifecycle.Event.ON_CREATE -> init()
Lifecycle.Event.ON_START -> start()
Lifecycle.Event.ON_DESTROY -> destroy()
else -> {}
}
}
}
3)注解方式 @OnLifecycleEvent(已废弃,面试可提)
- 写法简单但依赖反射/生成器,性能和可维护性都较差
- 已被官方标记为 Deprecated,只在老项目兼容时提及
三种方式对比:
| 方式 | 实现形式 | 性能 | 推荐度 | 适用场景 |
|---|---|---|---|---|
| DefaultLifecycleObserver | 接口方法 onCreate/onStart/... | 无反射,好 | ★★★ 首选 | 日常业务观察者,需类型安全与补全 |
| LifecycleEventObserver | 单一 onStateChanged + when(event) | 无反射,好 | ★★ 按需 | 统一入口、事件总线式桥接或调度 |
| @OnLifecycleEvent | 注解 + 反射/生成器 | 较差 | 不推荐,已废弃 | 仅兼容老项目、面试提一句 |
面试回答建议:
- 新项目一律首选
DefaultLifecycleObserver - 需要统一处理所有事件的场景可以用
LifecycleEventObserver - 注解方式只用于理解历史和兼容旧项目,不在新代码中使用
第二部分:Lifecycle 内部实现 & 高频原理面试题
2.1 ReportFragment:Lifecycle 如何「监听」 Activity
问题: Jetpack 如何在不改系统源码的前提下,感知 Activity 的生命周期?
核心答案: 通过在 Activity 中注入一个无 UI 的 Fragment,或注册 ActivityLifecycleCallbacks 来「旁路监听」。ComponentActivity 在 onCreate 里 根据 API 分支选择方式,因此只要继承 ComponentActivity(或 AppCompatActivity),生命周期监听就已挂上。
- API < 29:调用
ReportFragment.injectIfNeededIn(activity),往activity.getFragmentManager()里加一个无 UI 的ReportFragment,依靠 Fragment 与 Activity 生命周期同步,在 Fragment 的 onXxx 里dispatch(Event)。 - API ≥ 29:不再注入 ReportFragment,改为
activity.registerActivityLifecycleCallbacks,用 onActivityPostXxx(对应方法返回之后)发 ON_CREATE/ON_START/ON_RESUME,用 onActivityPreXxx(对应方法调用之前)发 ON_PAUSE/ON_STOP/ON_DESTROY,与 1.3 节「事件分发时机」一致,且避免使用过时 Fragment API。
关键点是:不侵入系统源码,又能 100% 捕获生命周期事件。 下图中的「Activity 生命周期回调」指系统对 Activity 调用的 onCreate/onStart 等;API ≥ 29 时由我们注册的 Callbacks 的 onActivityPostXxx/onActivityPreXxx 收到通知并 dispatch。
graph TB
A1[API29 Activity 回调] --> A2[ActivityLifecycleCallbacks]
A2 --> A3[dispatch Event]
B1[API28 Activity 生命周期] --> B2[ReportFragment 同步]
B2 --> B3[Fragment onXxx]
B3 --> B4[dispatch Event]
A3 --> C[handleLifecycleEvent]
B4 --> C
C --> D[更新 State]
D --> E[sync 分发 Observer]
(API29 即 29 及以上用 Callbacks,API28 即 28 及以下用 ReportFragment)
2.2 LifecycleRegistry:状态机 + 同步算法
面试中非常喜欢问 LifecycleRegistry 的两个点:
- 状态迁移如何计算?(
getStateAfter(event)) - 为什么
sync()里要while (!isSynced())的循环?
2.2.1 事件到状态的映射
示意逻辑(伪代码):
static State getStateAfter(Event event) {
switch (event) {
case ON_CREATE:
case ON_STOP:
return CREATED;
case ON_START:
case ON_PAUSE:
return STARTED;
case ON_RESUME:
return RESUMED;
case ON_DESTROY:
return DESTROYED;
case ON_ANY:
throw new IllegalArgumentException("ON_ANY must not be sent");
default:
throw new IllegalArgumentException("Unexpected event " + event);
}
}
考点:
说明你清楚 Event → State 的关系,以及 ON_ANY 不会由系统直接分发。注意:同一个 State 可能由不同 Event 到达(如 CREATED 既可由 ON_CREATE 到达,也可由 ON_STOP 到达),这样用「状态」描述「当前阶段」比只用「事件」更清晰。
状态流转总览(从系统到观察者):
graph LR
A[系统 onXxx] --> B[ReportFragment 或 Callbacks]
B --> C[handleLifecycleEvent]
C --> D[getStateAfter]
D --> E[更新 mState]
E --> F[sync]
F --> G{isSynced}
G -->|否| H[forwardPass 或 backwardPass]
H --> I[dispatchEvent]
I --> J[onStateChanged]
J --> F
G -->|是| K[结束]
2.2.2 sync() 为何要循环:最爱问的一题
问题描述:
LifecycleRegistry.sync() 内部不是「一次性遍历」,而是:
while (!isSynced()) {
// forwardPass / backwardPass ...
}
为什么不能只遍历一次?
思路:
在分发某个事件时,可能发生:
- 某个
Observer在回调里面添加新的观察者 - 某个
Observer在回调里面触发了新的生命周期事件 - 某个
Observer在回调里面移除了自己或其他观察者
如果只遍历一次:
- 刚刚添加的观察者会「错过历史事件」——状态落后于宿主
- 新事件导致
Lifecycle的真实状态前进/后退,但当前遍历并未感知 - 会出现状态不一致,甚至崩溃
graph TD
R[Registry 分发 ON_STOP] --> A[Observer A 收到 onStop]
A --> B[回调里 addObserver B]
B --> C[B 须补发事件追赶 下一轮 sync]
循环的意义:
- 每次循环检查「最老观察者 & 最新观察者」的状态是否与
Lifecycle一致 - 如果有人落后(需要前进)或超前(需要回退),就执行
forwardPass/backwardPass - 若在同步过程中有新的事件发生,标记
mNewEventOccurred = true,重新开始
graph LR
A[收事件] --> B[getStateAfter]
B --> C[更新 mState]
C --> D{已同步?}
D -->|否| E{回退?}
E -->|是| F[backwardPass]
E -->|否| G{前进?}
G -->|是| H[forwardPass]
F --> I[本轮结束]
H --> I
I --> D
D -->|是| J[结束]
总结一句可以直接在面试说的话:
sync()使用循环,是为了在「事件分发过程中观察者动态增删或再次触发事件」的情况下,仍然保证所有观察者最终与Lifecycle保持强一致状态。
2.2.3 双向渐进式同步:forwardPass 与 backwardPass
sync() 内部用两种「方向」把观察者状态与当前 mState 对齐:
| 方法 | 方向 | 何时用 | 事件来源 | 遍历顺序 |
|---|---|---|---|---|
| forwardPass | 状态上升(CREATED→STARTED→RESUMED) | 有观察者状态落后于 mState(需往前推) | 按 Event.upFrom(该观察者当前状态) 依次产生 ON_CREATE→ON_START→ON_RESUME | 从最先添加的观察者到最新添加的 |
| backwardPass | 状态下降(RESUMED→STARTED→CREATED) | 有观察者状态超前于 mState(需往后拉) | 按 Event.downFrom(该观察者当前状态) 依次产生 ON_PAUSE→ON_STOP→ON_DESTROY | 从最新添加的观察者到最先添加的 |
graph LR
F1[forwardPass eldest] --> F2[...] --> F3[newest 观察者]
B1[backwardPass newest] --> B2[...] --> B3[eldest 观察者]
- ObserverWithState:每个观察者被包成
ObserverWithState(observer, state)。dispatchEvent()时先根据事件算出「目标状态」,再回调observer.onStateChanged(),最后把该观察者的 state 更新为目标状态,保证状态不跳步。 - FastSafeIterableMap:用链表维护插入顺序,迭代时可安全增删,不会
ConcurrentModificationException。sync 后满足:先加入的观察者状态 ≥ 后加入的(eldest 状态 ≥ newest),所以「落后」的用 forwardPass 从 eldest 往 newest 推,「超前」的用 backwardPass 从 newest 往 eldest 拉。
2.3 新增 Observer 时的「状态追赶」
当你在一个已经 RESUMED 的 Activity 里添加一个新观察者:
lifecycle.addObserver(MyObserver())
问题: 它应该从哪里开始收到回调?
预期:它不应该只收到之后的新事件,而是要「补齐」到当前状态。
LifecycleRegistry.addObserver() 做了几件事:
- 新观察者初始状态设为:
- 如果宿主已
DESTROYED→ 直接DESTROYED - 否则从
INITIALIZED起步
- 如果宿主已
- 循环调用
dispatchEvent(...),用Event.upFrom(state)一步一步「追」到当前状态 - 追完之后,若非重入(当前不在
sync或addObserver的递归里),再调用sync()保证整体一致
(重入场景由 mAddingObserverCounter、mHandlingEvent 标记,避免在分发生命周期时再次 sync 造成死循环或状态错乱。)
graph LR
H[宿主 RESUMED] --> A[addObserver]
A --> B[初始 INITIALIZED]
B --> C[补发 ON_CREATE]
C --> D[补发 ON_START]
D --> E[补发 ON_RESUME]
E --> F[与宿主一致]
面试加分点:
提到「新增观察者会被补发生命周期事件,直到和宿主状态一致」。
补充:addObserver 时的适配
addObserver(observer) 传入的可能是 DefaultLifecycleObserver、LifecycleEventObserver 或已废弃的注解类。内部会通过 Lifecycling 统一包装成 LifecycleEventObserver(如 FullLifecycleObserverAdapter),再放入 ObserverWithState,这样 Registry 只需按一种接口分发生命周期事件。
第三部分:与协程/Flow 的现代集成(lifecycleScope 等)
这一部分是实际开发 & 面试的重头戏——如何正确使用:
lifecycleScopeviewLifecycleOwner.lifecycleScopeviewModelScoperepeatOnLifecycleflowWithLifecycle
3.1 常见作用域与 repeatOnLifecycle 的职责边界
| 概念 | 绑定对象 | 何时取消 | 适用场景 |
|---|---|---|---|
viewModelScope | ViewModel | ViewModel onCleared() | 业务逻辑、状态管理、数据请求 |
lifecycleScope | Activity/Fragment 实例 | onDestroy() | 与界面生命周期绑定的非 UI 任务(如埋点、日志) |
viewLifecycleOwner.lifecycleScope | Fragment 的视图生命周期 | onDestroyView() | 所有需要操作 View 的 UI 协程 |
repeatOnLifecycle | 任意 LifecycleOwner | 离开指定 state 时块内协程取消,DESTROYED 时所在 scope 终结 | 安全收集 Flow,状态感知的 UI 订阅 |
| ProcessLifecycleOwner.get() | 整个应用进程 | 随进程存在,不因某 Activity 销毁而取消 | 监听「应用进入前台/后台」 |
为何还要区分 viewModelScope? 配置变更(如旋转)时 Activity/Fragment 会重建,lifecycleScope 会随之取消;而 ViewModel 不会因配置变更销毁,viewModelScope 只有在 ViewModel 被 clear 时才取消,因此业务逻辑、网络请求等应放在 ViewModel 里用 viewModelScope,界面层才用 lifecycleScope / viewLifecycleOwner.lifecycleScope。
graph TD
Start[需要启动协程或收集 Flow] --> Q1{在 Fragment 中}
Q1 -->|是| Q2{会操作 View}
Q2 -->|是| A1[viewLifecycleOwner lifecycleScope]
Q2 -->|否| A2[lifecycleScope]
Q1 -->|否| Q3{是 ViewModel 内业务}
Q3 -->|是| A3[viewModelScope]
Q3 -->|否| A4[lifecycleScope]
A1 --> Q4{收集 Flow}
A4 --> Q4
Q4 -->|是| A5[repeatOnLifecycle STARTED]
Q4 -->|否| End[按需选择]
A5 --> End
在 Fragment 中的金科玉律:
- 更新 UI → 必须用
viewLifecycleOwner.lifecycleScope - 仅和 Fragment 实例绑定(如日志、埋点) → 可用
lifecycleScope - Flow →
viewLifecycleOwner.lifecycleScope + repeatOnLifecycle(Lifecycle.State.STARTED)
graph LR
A[onCreate] --> B[onCreateView]
B --> C[onViewCreated]
C --> D[onStart]
D --> E[onResume]
E --> F[onPause]
F --> G[onStop]
G --> H[onDestroyView]
H --> I[onDestroy]
注:lifecycleScope 在 onDestroy 取消;viewLifecycleOwner.lifecycleScope 在 onDestroyView 取消。
关键:View 在
onDestroyView时已销毁,但 Fragment 实例还在(例如配置变更时系统会保留 Fragment、只重建 View)。若用lifecycleScope更新 UI,在onDestroyView与onDestroy之间协程仍可能执行,就会访问到已销毁的 View,导致崩溃。
3.2 Fragment UI 崩溃的典型坑 & 正确写法
错误示例:在 Fragment 的 lifecycleScope 里更新 UI
class ExampleFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
delay(3000)
// 配置变更或返回栈操作后,View 可能已经被销毁
textView.text = "Loaded" // ⚠️ 可能 NPE / IllegalStateException
}
}
}
正确示例:使用 viewLifecycleOwner.lifecycleScope
class ExampleFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
delay(3000)
// 如果视图销毁,对应 scope 已被取消,不会执行到这里
textView.text = "Loaded" // ✅ UI 安全
}
}
}
对比小结:
| 特性 | lifecycleScope | viewLifecycleOwner.lifecycleScope |
|---|---|---|
| 宿主 | Fragment 实例 | Fragment 的视图生命周期(viewLifecycleOwner) |
| 取消时机 | onDestroy() | onDestroyView() |
| 配置变更后 | 保持,可能访问已销毁 View | 被取消,安全 |
| 使用场景 | 非 UI 任务(埋点、同步等) | 所有 UI 相关协程 |
- 结论:凡会操作 View 的协程,必须用
viewLifecycleOwner.lifecycleScope,避免在已销毁 View 上更新导致崩溃。
3.3 launchWhenX 已废弃,为什么必须换成 repeatOnLifecycle
旧代码常见写法(不推荐):
lifecycleScope.launchWhenStarted {
viewModel.dataFlow.collect { value ->
updateUI(value)
}
}
launchWhenX 的问题:
- 当生命周期降到
STARTED以下时,协程是**挂起(suspended)**而不是取消 - 上游 Flow 仍然在产生数据(可能是昂贵操作),只是下游不处理
- 既浪费资源,又容易积压数据
推荐写法:repeatOnLifecycle
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.dataFlow.collect { value ->
updateUI(value)
}
}
}
优点:
- 生命周期 <
STARTED时,内部协程和 Flow 会被取消,资源被释放 - 回到
STARTED及以上时,会重新启动收集(自动重订阅) - 与 Flow 的取消机制天然对齐,满足背压 & 资源回收
graph TB
L1[launchWhenX 生命周期大于等于STARTED] --> L2[collect 执行]
L2 --> L3[生命周期小于STARTED]
L3 --> L4[协程挂起]
L4 --> L5[Flow 仍生产 浪费资源]
R1[repeatOnLifecycle 生命周期大于等于STARTED] --> R2[collect 执行]
R2 --> R3[生命周期小于STARTED]
R3 --> R4[协程取消]
R4 --> R5[Flow 停止收集]
对比小结:
| 特性 | launchWhenX(已废弃) | repeatOnLifecycle(推荐) |
|---|---|---|
| 低于 STARTED | 协程挂起 | 协程取消 |
| Flow 状态 | 继续生产,浪费资源 | 停止收集 |
| 回到前台 | 恢复执行 | 重新执行 block |
| 官方态度 | 废弃 | 推荐 |
面试话术:
launchWhenX只挂起协程,上游 Flow 仍运行;repeatOnLifecycle会取消协程并停止收集,是官方推荐的 Flow + Lifecycle 用法。
3.4 flowWithLifecycle 的简洁封装
基于 repeatOnLifecycle 的链式写法,语义与 repeatOnLifecycle 等价,只是把「在哪个 state 下收集」挂在 Flow 上,可读性更好;与 3.3 的写法二选一即可。
viewLifecycleOwner.lifecycleScope.launch {
viewModel.dataFlow
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.collect { updateUI(it) }
}
第四部分:典型实战场景与最佳实践
除了系统提供的 Activity/Fragment,我们也可以让 Dialog、自定义业务组件 具备生命周期感知能力(ViewModel 安全感知视图生命周期的写法见第六部分 Q9)。下面是最常见的几类用法。
4.1 避免 Dialog 泄漏:让 Dialog 也「有生命周期」
典型问题:Activity 旋转或退出时,若 Dialog 未在宿主销毁前 dismiss(),会报 WindowLeaked 或泄漏。传统写法是手动在 Activity 的 onDestroy 里调 dismiss(),容易漏写或重复写。
核心思路: 让 Dialog 自己观察宿主的 Lifecycle,在 ON_DESTROY 时自动 dismiss 并移除观察者,一次绑定、处处生效。
- 让 Dialog 实现
LifecycleEventObserver,在 show 时绑定到宿主 Activity 的lifecycle - 在
ON_DESTROY时自动dismiss()并removeObserver(this),避免泄漏与重复释放
对比小结:
| 方式 | 做法 | 问题/优点 |
|---|---|---|
| 传统 | 在 Activity onDestroy 里手动 if (dialog != null) dialog.dismiss() | 易漏写、多 Dialog 时重复、旋转重建要自己判断 |
| Lifecycle | Dialog 实现 LifecycleEventObserver,show 时 addObserver,ON_DESTROY 时 dismiss + removeObserver | 一次绑定,任意销毁路径都会自动释放,无泄漏 |
核心代码示意:
// 构造或 show 时绑定
(context as? ComponentActivity)?.lifecycle?.addObserver(this)
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (isShowing) dismiss()
source.lifecycle.removeObserver(this)
}
}
4.2 自定义 LifecycleOwner:给「纯业务模块」配上生命周期
常见问题:
- 下载管理器、播放器、即时通信连接等属于业务组件
- 但它们也有自己的「生命周期」:启动、暂停、恢复、销毁
- 不想靠外部手动调用
start/stop/destroy,希望能像 Activity 一样拥有 Lifecycle
做法(四步): ① 持有 LifecycleRegistry ② 实现 getLifecycle() ③ 初始化状态 ④ 在业务方法里主动调用 handleLifecycleEvent(Event)。与 Activity 不同:Activity 由系统调 onStart/onStop 等,再经 ReportFragment/Callbacks 转成事件;这里没有系统驱动,何时发什么事件完全由业务代码决定(例如 start() 里发 ON_START、ON_RESUME,destroy() 里发 ON_DESTROY)。
class DownloadController : LifecycleOwner {
private val lifecycleRegistry = LifecycleRegistry(this)
override fun getLifecycle(): Lifecycle = lifecycleRegistry
init { lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED }
fun start() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
fun destroy() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
}
这样你可以:
- 复用
LifecycleObserver的能力(比如业务模块里也跑协程) - 在宿主 Activity 中把它当成一个「次级 LifecycleOwner」来组合使用(例如在 Activity 的
onStart里lifecycle.addObserver(downloadController),在onDestroy里removeObserver并调用downloadController.destroy())
第五部分:性能优化与内存安全实践
5.1 大量 Observer 会不会卡?怎么优化?
理论上:
- 每次生命周期事件(如 onStart、onStop)都会触发一次 对所有观察者的遍历,因此是 O(N),N 为观察者数量。N 通常不大,一般不会成为瓶颈
- 若观察者很多(如数十个)或单个回调里做了很重的工作,主线程上会累加延迟,甚至卡顿、ANR
优化策略:
- 合并观察者:把同类逻辑放到一个
CompositeObserver中,统一调度 - 懒加载:只有在功能真正启用时才
addObserver - 按需移除:功能关闭或页面离开时,及时
removeObserver - 避免在回调中执行重 CPU 操作:重任务丢到后台协程或线程池
加分点:
- 数据埋点、日志、Crash 监控这类「低优先级」逻辑,建议集中封装成一个观察者,并且其内部逻辑异步执行
一句话小结: 观察者过多时优先「合并 + 按需注册 + 回调里不跑重活」。
5.2 常见内存泄漏来源 & Lifecycle 防线
典型泄漏来源:
- 匿名内部类/ lambda 捕获
Activity/Fragment引用 - 长生命周期对象(单例)持有
Context或View强引用 - 协程/线程在宿主销毁后仍然运行,并访问 UI
结合 Lifecycle 的防线:
- 在
DefaultLifecycleObserver.onDestroy()里统一本地资源清理:- 取消协程 Job
- 关闭连接、停止监听、释放缓存
- 在自定义可复用组件中,只持有 弱引用 或使用
ApplicationContext - 对所有需要 UI 安全的协程,统一用:
- Fragment:
viewLifecycleOwner.lifecycleScope - Activity:
lifecycleScope + repeatOnLifecycle(STARTED)
- Fragment:
一句话小结: 用 Lifecycle 做「统一收尾点」:onDestroy 里取消 Job、释放资源、removeObserver;组件内少持强引用 Context/View。
第六部分:高频面试问题 & 标准回答模板
下面整理一些常见面试题,你可以直接拿来当「问答模板」。
Q1:Lifecycle 解决了什么问题?相比传统方式的优势是什么?
建议回答要点:
- 解决了生命周期感知能力复用的问题:业务组件不再强依赖具体 Activity/Fragment,转而依赖抽象的
LifecycleOwner - 降低了内存泄漏风险:组件可以在
ON_STOP/ON_DESTROY自动释放资源 - 改善了代码结构:控制器负责 UI,观察者负责业务逻辑和资源管理
- 为协程、Flow、ViewModel 提供了统一的生命周期基础设施(
lifecycleScope、repeatOnLifecycle等)
Q2:请解释 Lifecycle 的三个核心角色以及它们之间的关系
简洁回答:
LifecycleOwner:生命周期拥有者(如 Activity、Fragment),提供getLifecycle()入口Lifecycle:被观察的对象,保存当前 State/Event,唯一实现是LifecycleRegistryLifecycleObserver:观察者,订阅 Lifecycle 的状态变化并做出响应
关系一句话:
LifecycleOwner持有Lifecycle,观察者注册到Lifecycle;当 Owner 生命周期变化时,由LifecycleRegistry更新状态并向所有LifecycleObserver分发事件。
Q3:Lifecycle 如何感知 Activity 的生命周期?低版本和高版本有什么差异?
回答要点:
- ComponentActivity 在 onCreate 里根据 API 选择监听方式:API < 29 调用
ReportFragment.injectIfNeededIn(activity),API ≥ 29 改用activity.registerActivityLifecycleCallbacks,不再注入 Fragment - API < 29:往
activity.getFragmentManager()里加一个无 UI 的ReportFragment,依靠 Fragment 与 Activity 生命周期同步,在 Fragment 的 onXxx 里 dispatch 事件 - API ≥ 29:用 onActivityPostXxx(方法返回后)发进入类事件、onActivityPreXxx(方法调用前)发离开类事件,与 1.3「事件分发时机」一致,且避免过时 Fragment API
Q4:为什么 LifecycleRegistry.sync() 要用 while 循环?说一个可能导致问题的场景
场景示例:
- 在分发
ON_STOP事件时,某个Observer的回调中又注册了一个新的观察者 - 新增的观察者需要「补齐历史事件」到当前状态
- 如果只是单次遍历,它永远不会同步到正确状态
总结句:
while 循环是为了在「观察者可动态增删、回调中可能再触发事件」的情况下,仍然保证所有观察者最终状态与 Lifecycle 一致。
Q5:Fragment 中 lifecycleScope 和 viewLifecycleOwner.lifecycleScope 有什么区别?如何选择?
| 特性 | lifecycleScope | viewLifecycleOwner.lifecycleScope |
|---|---|---|
| 宿主 | Fragment 实例 | Fragment 的视图生命周期 |
| 取消时机 | onDestroy() | onDestroyView() |
| UI 安全 | 否(View 可能已销毁) | 是 |
| 适用 | 非 UI 长任务 | 所有 UI 相关操作 |
结论:操作 View 的协程一律用
viewLifecycleOwner.lifecycleScope。
Q6:launchWhenStarted 与 repeatOnLifecycle 的区别?为什么推荐后者?
| 维度 | launchWhenStarted | repeatOnLifecycle |
|---|---|---|
| 生命周期 < STARTED 时 | 协程挂起 | 协程取消 |
| Flow | 继续生产 | 停止收集 |
| 资源 | 浪费 | 释放 |
| 官方 | 已废弃 | 推荐 |
核心:前者只挂起,Flow 仍运行;后者取消协程,从源头停止收集。
Q7:Lifecycle 事件是在生命周期方法「之前」还是「之后」分发?为什么这样设计?
- ON_CREATE / ON_START / ON_RESUME:在对应方法返回之后分发 → 观察者收到时状态已进入新阶段,适合做「开始/恢复」逻辑。
- ON_PAUSE / ON_STOP / ON_DESTROY:在对应方法调用之前分发 → 观察者收到时还在当前状态,适合做「暂停/释放」逻辑,避免在方法执行完后再收尾导致竞态。
设计意图:进入新状态后再通知「可以做事」,离开前通知「该收尾了」,与 1.3 节一致。
Q8:FastSafeIterableMap 和普通 Map 有什么区别?为什么 Lifecycle 用前者?
- 顺序:保持插入顺序,便于按添加顺序做 forwardPass/backwardPass。
- 迭代安全:在遍历观察者分发生命周期时,回调里可能 add/remove 观察者;普通 Map/集合在迭代过程中修改会抛
ConcurrentModificationException,FastSafeIterableMap 通过链表结构在迭代中安全增删。 - 不变性:sync 后「先添加的观察者状态 ≥ 后添加的」,便于用 eldest/newest 判断需要 forward 还是 backward。
Q9:如何在 ViewModel 中安全感知 Fragment 的视图生命周期?
回答要点:
- Fragment 在
onViewCreated里把viewLifecycleOwner传给 ViewModel(例如通过MutableLiveData<LifecycleOwner>或setViewLifecycleOwner(owner)方法) - ViewModel 内用该
owner.lifecycle.addObserver(...),在onStart/onStop等回调里执行需要「视图存在」的逻辑(如刷新 UI、轮询) - 在 Observer 的
onDestroy(owner)里removeObserver(this)并取消相关 Job,避免泄漏 - 这样 ViewModel 不直接持 Fragment,只持 LifecycleOwner,且该 Owner 在
onDestroyView后失效,协程/任务会随视图销毁而取消
结论:通过「Fragment 注入 viewLifecycleOwner → ViewModel 观察该 Lifecycle」即可安全感知视图生命周期;Fragment 侧用法见第三部分 viewLifecycleOwner 与作用域选择。
总结:如何在项目中「用好」 Lifecycle
按需阅读建议: 只背面试题 → 第六部分 + 常见坑一览;要搞懂「为什么这样设计」→ 第一、二部分;做项目选 scope、写 Flow → 第三部分 + 实践 checklist;做性能与内存优化 → 第五部分。
常见坑一览:
| 坑 | 后果 | 正确做法 |
|---|---|---|
Fragment 里用 lifecycleScope 更新 UI | 配置变更/返回后可能访问已销毁 View,崩溃 | 用 viewLifecycleOwner.lifecycleScope |
用 launchWhenStarted 收集 Flow | 后台 Flow 仍生产,浪费资源 | 用 repeatOnLifecycle(STARTED) |
| 单例或长生命周期对象持有 Activity/Fragment | 内存泄漏 | 弱引用或 ApplicationContext,并在 onDestroy 解绑 |
| 只 addObserver 从不 removeObserver | 重复注册或泄漏 | 在 onDestroy 中 remove,或确保 Observer 不持有宿主 |
| 在 Observer 回调里做重量级或阻塞操作 | 主线程卡顿、ANR | 重逻辑放到协程/线程池,回调只做轻量派发 |
常用依赖(Gradle):
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0" // lifecycleScope、repeatOnLifecycle 等
implementation "androidx.lifecycle:lifecycle-common-java8:2.7.0" // DefaultLifecycleObserver
实践 checklist:
- UI 层
- Fragment 中 UI 协程:一律
viewLifecycleOwner.lifecycleScope + repeatOnLifecycle(STARTED) - Activity 中收集 Flow:
lifecycleScope + repeatOnLifecycle(STARTED)
- Fragment 中 UI 协程:一律
- 业务组件
- 把「需要跟随生命周期」的逻辑封装进
DefaultLifecycleObserver - 通过
lifecycle.addObserver(...)注入,而不是直接写在Activity/Fragment里
- 把「需要跟随生命周期」的逻辑封装进
- 协程 & Flow
- 废弃
launchWhenX系列,统一迁移到repeatOnLifecycle - 所有长时间运行任务都要考虑在
ON_DESTROY时取消
- 废弃
- 调试 & 排查
- 一旦出现「后台仍在执行任务」「视图销毁后更新 UI」这类问题,先检查:
- 是否用错了
lifecycleScope/viewLifecycleOwner.lifecycleScope - 是否缺少
repeatOnLifecycle/flowWithLifecycle - 是否在合适生命周期自动释放资源
- 是否用错了
- 一旦出现「后台仍在执行任务」「视图销毁后更新 UI」这类问题,先检查: