Android Jetpack - 1 Lifecycle

150 阅读24分钟

前言:为什么 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
  • 如:ComponentActivityFragmentProcessLifecycleOwner(应用前后台:ON_START 表示至少一个 Activity 可见、ON_STOP 表示全进后台)、自定义 LifecycleOwner

2)Lifecycle(生命周期)

  • 抽象类,内部维护当前状态(State)和事件(Event);唯一实现类是 LifecycleRegistry,负责状态机与事件分发。我们日常拿到的 getLifecycle() 返回的就是 LifecycleRegistry 实例。

3)LifecycleObserver(观察者)

  • 任何想「跟随」LifecycleOwner 生命周期的组件
  • 通过 LifecycleOwner.getLifecycle().addObserver(...) 注册

关系:

  • LifecycleOwner 持有一个 Lifecycle
  • LifecycleObserver 注册到 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:对象已创建,尚未执行 onCreate
    • CREATED:已执行 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新状态
打开 ActivityonCreate()ON_CREATECREATED
从不可见变为可见onStart()ON_STARTSTARTED
完全显示、可点击onResume()ON_RESUMERESUMED
被弹窗盖住 / 按 HomeonPause()ON_PAUSESTARTED
完全不可见(切到别的 App)onStop()ON_STOPCREATED
关闭或 finish()onDestroy()ON_DESTROYDESTROYED

因此:按 Home 会先 onPause(RESUMED→STARTED),再 onStop(STARTED→CREATED);从后台返回则先 onStartonResume。状态可前进也可回退。

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) 判断是否至少可见,不用自己维护标志位;lifecycleScoperepeatOnLifecycle 等也都基于这套状态机。

为什么不能只要一个?

  • 只有 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 来「旁路监听」。ComponentActivityonCreate 里 根据 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 的两个点:

  1. 状态迁移如何计算?getStateAfter(event)
  2. 为什么 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() 做了几件事:

  1. 新观察者初始状态设为:
    • 如果宿主已 DESTROYED → 直接 DESTROYED
    • 否则从 INITIALIZED 起步
  2. 循环调用 dispatchEvent(...),用 Event.upFrom(state) 一步一步「追」到当前状态
  3. 追完之后,若非重入(当前不在 syncaddObserver 的递归里),再调用 sync() 保证整体一致

(重入场景由 mAddingObserverCountermHandlingEvent 标记,避免在分发生命周期时再次 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) 传入的可能是 DefaultLifecycleObserverLifecycleEventObserver 或已废弃的注解类。内部会通过 Lifecycling 统一包装成 LifecycleEventObserver(如 FullLifecycleObserverAdapter),再放入 ObserverWithState,这样 Registry 只需按一种接口分发生命周期事件。


第三部分:与协程/Flow 的现代集成(lifecycleScope 等)

这一部分是实际开发 & 面试的重头戏——如何正确使用:

  • lifecycleScope
  • viewLifecycleOwner.lifecycleScope
  • viewModelScope
  • repeatOnLifecycle
  • flowWithLifecycle

3.1 常见作用域与 repeatOnLifecycle 的职责边界

概念绑定对象何时取消适用场景
viewModelScopeViewModelViewModel onCleared()业务逻辑、状态管理、数据请求
lifecycleScopeActivity/Fragment 实例onDestroy()与界面生命周期绑定的非 UI 任务(如埋点、日志)
viewLifecycleOwner.lifecycleScopeFragment 的视图生命周期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,在 onDestroyViewonDestroy 之间协程仍可能执行,就会访问到已销毁的 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 安全
        }
    }
}

对比小结:

特性lifecycleScopeviewLifecycleOwner.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 时重复、旋转重建要自己判断
LifecycleDialog 实现 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 的 onStartlifecycle.addObserver(downloadController),在 onDestroyremoveObserver 并调用 downloadController.destroy()

第五部分:性能优化与内存安全实践

5.1 大量 Observer 会不会卡?怎么优化?

理论上:

  • 每次生命周期事件(如 onStart、onStop)都会触发一次 对所有观察者的遍历,因此是 O(N),N 为观察者数量。N 通常不大,一般不会成为瓶颈
  • 若观察者很多(如数十个)或单个回调里做了很重的工作,主线程上会累加延迟,甚至卡顿、ANR

优化策略:

  • 合并观察者:把同类逻辑放到一个 CompositeObserver 中,统一调度
  • 懒加载:只有在功能真正启用时才 addObserver
  • 按需移除:功能关闭或页面离开时,及时 removeObserver
  • 避免在回调中执行重 CPU 操作:重任务丢到后台协程或线程池

加分点:

  • 数据埋点、日志、Crash 监控这类「低优先级」逻辑,建议集中封装成一个观察者,并且其内部逻辑异步执行

一句话小结: 观察者过多时优先「合并 + 按需注册 + 回调里不跑重活」。


5.2 常见内存泄漏来源 & Lifecycle 防线

典型泄漏来源:

  • 匿名内部类/ lambda 捕获 Activity / Fragment 引用
  • 长生命周期对象(单例)持有 ContextView 强引用
  • 协程/线程在宿主销毁后仍然运行,并访问 UI

结合 Lifecycle 的防线:

  • DefaultLifecycleObserver.onDestroy() 里统一本地资源清理:
    • 取消协程 Job
    • 关闭连接、停止监听、释放缓存
  • 在自定义可复用组件中,只持有 弱引用 或使用 ApplicationContext
  • 对所有需要 UI 安全的协程,统一用:
    • Fragment:viewLifecycleOwner.lifecycleScope
    • Activity:lifecycleScope + repeatOnLifecycle(STARTED)

一句话小结: 用 Lifecycle 做「统一收尾点」:onDestroy 里取消 Job、释放资源、removeObserver;组件内少持强引用 Context/View。


第六部分:高频面试问题 & 标准回答模板

下面整理一些常见面试题,你可以直接拿来当「问答模板」。

Q1:Lifecycle 解决了什么问题?相比传统方式的优势是什么?

建议回答要点:

  1. 解决了生命周期感知能力复用的问题:业务组件不再强依赖具体 Activity/Fragment,转而依赖抽象的 LifecycleOwner
  2. 降低了内存泄漏风险:组件可以在 ON_STOP / ON_DESTROY 自动释放资源
  3. 改善了代码结构:控制器负责 UI,观察者负责业务逻辑和资源管理
  4. 为协程、Flow、ViewModel 提供了统一的生命周期基础设施lifecycleScoperepeatOnLifecycle 等)

Q2:请解释 Lifecycle 的三个核心角色以及它们之间的关系

简洁回答:

  • LifecycleOwner生命周期拥有者(如 Activity、Fragment),提供 getLifecycle() 入口
  • Lifecycle被观察的对象,保存当前 State/Event,唯一实现是 LifecycleRegistry
  • LifecycleObserver观察者,订阅 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 中 lifecycleScopeviewLifecycleOwner.lifecycleScope 有什么区别?如何选择?

特性lifecycleScopeviewLifecycleOwner.lifecycleScope
宿主Fragment 实例Fragment 的视图生命周期
取消时机onDestroy()onDestroyView()
UI 安全否(View 可能已销毁)
适用非 UI 长任务所有 UI 相关操作

结论:操作 View 的协程一律用 viewLifecycleOwner.lifecycleScope


Q6:launchWhenStartedrepeatOnLifecycle 的区别?为什么推荐后者?

维度launchWhenStartedrepeatOnLifecycle
生命周期 < 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)
  • 业务组件
    • 把「需要跟随生命周期」的逻辑封装进 DefaultLifecycleObserver
    • 通过 lifecycle.addObserver(...) 注入,而不是直接写在 Activity / Fragment
  • 协程 & Flow
    • 废弃 launchWhenX 系列,统一迁移到 repeatOnLifecycle
    • 所有长时间运行任务都要考虑在 ON_DESTROY 时取消
  • 调试 & 排查
    • 一旦出现「后台仍在执行任务」「视图销毁后更新 UI」这类问题,先检查:
      • 是否用错了 lifecycleScope / viewLifecycleOwner.lifecycleScope
      • 是否缺少 repeatOnLifecycle / flowWithLifecycle
      • 是否在合适生命周期自动释放资源