编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机的原理
  • 操作系统原理
  • 网络协议原理
  • 数据库的原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 技能之旅
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Kotlin精通
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机的原理
  • 操作系统原理
  • 网络协议原理
  • 数据库的原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 技能之旅
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Kotlin精通
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 序卷方法论

  • 数据的本质

  • 运行时模型

  • 并发的设计

  • 内存的真相

  • 交互和系统

    • README
    • 1.窗口核心设计思想
    • 2.视图加载渲染设计
    • 3.图形渲染管线原理
    • 4.手势事件设计灵魂
    • 5.消息机制设计思想
    • 6.跨进程的通信设计
    • 7.组件生命周期管理
      • 目录
      • 0.生命周期事故
        • 0.1 案例代码
        • 0.2 四个核心问题
        • 0.3 事故启示
        • 0.4 认知地图
      • 1.为何需要生命周期
        • 1.1 纯函数 vs 长生命
        • 1.2 三大根因
        • 1.3 设计目标
      • 2.生命周期四件套
        • 2.1 状态机
        • 2.2 钩子
        • 2.3 所有权
        • 2.4 可见性
        • 2.5 跨端名词矩阵
      • 3.Android 生命周期
        • 3.1 Activity 七大回调
        • 3.2 Fragment 13 回调
        • 3.3 配置变更与恢复
        • 3.4 ViewModel 解耦
      • 4.iOS 生命周期
        • 4.1 UIViewController 回调
        • 4.2 ARC 与 deinit
        • 4.3 SwiftUI 声明式
      • 5.前端生命周期
        • 5.1 React Class 回调
        • 5.2 React useEffect
        • 5.3 Vue Options 钩子
        • 5.4 Vue Composition API
      • 6.函数式生命周期
        • 6.1 Flutter Stateful 11 阶段
        • 6.2 Compose remember
        • 6.3 函数式 vs 命令式
      • 7.横向对比矩阵
        • 7.1 能力对齐表
        • 7.2 设计哲学站位
      • 8.反模式与陷阱
        • 8.1 onPause 耗时
        • 8.2 异步回调悬垂
        • 8.3 Fragment 强引用
        • 8.4 闭包循环引用
        • 8.5 忘记反注册
      • 9.现代演进
        • 9.1 LifecycleOwner 绑定
        • 9.2 协程作用域
        • 9.3 Combine 自动取消
        • 9.4 副作用与生命周期
      • 10.心智与哲学
        • 10.1 跨端术语对照
        • 10.2 本卷章节呼应
        • 10.3 通用心智口诀
    • 8.页面导航与路由设计
    • 9.响应式数据绑定设计
    • 10.国际化适配的设计
  • 内功
  • 交互和系统
杨充
2026-06-28
目录

7.组件生命周期管理

# 7.组件生命周期管理

📍 本篇位置:第 5 卷 · 交互与系统 · 第 7 篇(与 5.1「窗口」、5.2「视图加载」一脉相承——5.1 解决「容器从哪里来」,5.2 解决「视图怎么挂上去」,5.7 解决「容器在时间维度上怎么活、怎么死」) 🎯 核心矛盾:「组件想长寿」 vs 「系统资源有限要让它死」——开发者希望组件「一直在那里、状态永远保留、随取随用」;但 OS 必须在内存/电量/可见性等多重压力下「优雅地让组件睡眠、回收、复活」。两难解法只有一个:把组件的「时间维度」拆成有限状态机,每个状态切换给开发者一个钩子——这就是生命周期(Lifecycle) 🧭 设计灵魂:生命周期 = 「状态机 + 钩子 + 所有权 + 可见性」四件套。任何组件框架(Android/iOS/React/Vue/Flutter/Compose/SwiftUI)都在这四件套上做选择——理解了通用骨架,所有平台的生命周期都是同一棵树的不同枝叶 🌐 跨平台覆盖:Android (Activity / Fragment / View / Service / ViewModel / Compose) · iOS (UIViewController / UIView / SwiftUI View / App lifecycle) · 前端 (React Class / Hooks / Vue Options / Vue Composition / Svelte / Angular) · 跨端 (Flutter StatefulWidget / RN / Compose Multiplatform) · 后端 (Spring Bean / @PostConstruct / @PreDestroy) · 桌面 (WPF / Qt / Electron) 🔗 延伸阅读:← 5.1 窗口核心设计思想 (opens new window) · ← 5.2 视图加载渲染设计 (opens new window) · ← 5.5 消息机制设计思想 (opens new window) · ← 5.6 跨进程通信设计 (opens new window) · → 5.8 页面导航与路由设计 (opens new window) · ↔ 3.x 并发之道 · ↔ 4.x 内存的真相 💡 通用心智:忘掉 onCreate/viewDidLoad/componentDidMount/initState 这些具体名词,记住一句话——生命周期 = 「组件在『出生 → 可见 → 交互 → 不可见 → 死亡』五段时间轴上的有限状态机,每段切换给一个钩子让你做资源管理」。Android 13 个回调、iOS 5 个回调、React 一个 useEffect、Vue 8 个钩子、Compose 的 DisposableEffect——本质都是同一棵树。核心理解:① 生命周期回调不是给你"业务用"的,是给你"资源管理"用的(订阅/解绑、注册/反注册);② 任何"耗时操作"放在错误的回调里都是事故(onPause 做存盘 = 黑屏);③ 组件销毁不等于对象释放——这是 90% 内存泄漏的根因——把这三点想明白,所有生命周期问题就有答案了。


# 目录

  • 0.生命周期事故
  • 1.为何需要生命周期
    • 1.1 纯函数 vs 长生命
    • 1.2 三大根因
    • 1.3 设计目标
  • 2.生命周期四件套
    • 2.1 状态机
    • 2.2 钩子
    • 2.3 所有权
    • 2.4 可见性
    • 2.5 跨端名词矩阵
  • 3.Android 生命周期
    • 3.1 Activity 七大回调
    • 3.2 Fragment 13 回调
    • 3.3 配置变更与恢复
    • 3.4 ViewModel 解耦
  • 4.iOS 生命周期
    • 4.1 UIViewController 回调
    • 4.2 ARC 与 deinit
    • 4.3 SwiftUI 声明式
  • 5.前端生命周期
    • 5.1 React Class 回调
    • 5.2 React useEffect
    • 5.3 Vue Options 钩子
    • 5.4 Vue Composition API
  • 6.函数式生命周期
    • 6.1 Flutter Stateful 11 阶段
    • 6.2 Compose remember
    • 6.3 函数式 vs 命令式
  • 7.横向对比矩阵
    • 7.1 能力对齐表
    • 7.2 设计哲学站位
  • 8.反模式与陷阱
    • 8.1 onPause 耗时
    • 8.2 异步回调悬垂
    • 8.3 Fragment 强引用
    • 8.4 闭包循环引用
    • 8.5 忘记反注册
  • 9.现代演进
    • 9.1 LifecycleOwner 绑定
    • 9.2 协程作用域
    • 9.3 Combine 自动取消
    • 9.4 副作用与生命周期
  • 10.心智与哲学
    • 10.1 跨端术语对照
    • 10.2 本卷章节呼应
    • 10.3 通用心智口诀

# 0.生命周期事故

场景:某电商 App 商品详情页,QA 报告:「随机概率黑屏 + Crash,无规律复现」。线上 Crashlytics 显示 IllegalStateException: Fragment not attached to a context,崩溃量占总崩溃 23%。

# 0.1 案例代码

class ProductDetailFragment : Fragment() {
    private lateinit var productId: String
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        productId = arguments?.getString("id") ?: ""
        
        // ★ 网络请求在 onCreate 启动
        ApiService.getProduct(productId) { product ->
            // ★ 回调可能在任何时刻到来——5s 后、10s 后、用户已经关闭页面
            updateUI(product)  // ❌ 如果 Fragment 已经 detach,这里崩溃
            
            // ★ 本地存储——埋雷点 2
            saveToCache(product)  // 主线程 IO,可能 ANR
        }
        
        // ★ 注册全局事件总线
        EventBus.register(this)  // ❌ 没在 onDestroy 反注册
    }
    
    override fun onPause() {
        super.onPause()
        // ★ 在 onPause 做存盘操作
        SharedPreferences.edit().putString("last_id", productId).commit()  // ❌ 阻塞主线程
    }
    
    private fun updateUI(product: Product) {
        // ★ 直接用 context / activity,没做存活检查
        val tv = view?.findViewById<TextView>(R.id.title)
        tv?.text = product.title
        Glide.with(requireContext()).load(product.image).into(imageView)  // ❌ requireContext 抛异常
    }
}

# 0.2 四个核心问题

症状链:
    用户进入商品页 → onCreate 发起网络请求 → 用户立刻按返回 → onDestroyView/onDetach 触发 →
    网络回调 5s 后才到 → updateUI() 调用 requireContext() →
    ★ Fragment 已不再附加到 Activity → 抛 IllegalStateException → Crash

四个埋雷:
    1. 异步回调没做"组件存活检查"
        - 回调里直接用 requireContext()/requireActivity()
        - 没用 viewLifecycleOwner / lifecycleScope
    
    2. onPause 做了同步 IO
        - SharedPreferences.commit() 是阻塞调用
        - 主线程做 IO → 用户感知到"按返回慢半拍" → 进程被压后台 → ANR
    
    3. EventBus.register 但没 unregister
        - Fragment 被销毁了,但 EventBus 还持有它的强引用
        - 内存泄漏 + 后续事件触发死对象
    
    4. 把"业务逻辑"塞进生命周期回调
        - onCreate 不是用来发网络请求的(应该是 ViewModel + Repository)
        - onPause 不是用来存盘的(应该是异步 + 应用层节流)

# 0.3 事故启示

本质:开发者把"生命周期回调"当成了"业务执行点",把"组件引用"当成了"永生对象"

正确认知:
    ✓ 生命周期回调是「资源管理点」——订阅时机/解绑时机/资源回收时机
    ✓ 异步任务必须与组件生命周期"绑定",组件死则任务死
    ✓ 任何"组件持有"都要考虑"持有方比被持有方活得长"的情况
    ✓ 主线程 ≠ 万能线程——任何"看起来慢"的操作都不能放主线程
    ✓ 组件销毁 ≠ 对象释放——GC 不释放,是因为有人还在持有它

修复后的代码(5 处关键变更):
    1. ApiService.getProduct → viewModel.loadProduct + viewLifecycleOwner.lifecycleScope
    2. updateUI 用 LiveData.observe(viewLifecycleOwner) 自动管理
    3. onPause 的存盘 → DataStore async + onStop 触发
    4. EventBus → SharedFlow 收集在 lifecycleScope 里(自动取消)
    5. 所有 requireContext() → 检查 isAdded + viewLifecycleOwnerLiveData

# 0.4 认知地图

事故是「果」,生命周期设计是「因」。本篇将沿着这条线展开:

  §0 这个事故 ── 看懂"生命周期事故"的 5 个埋雷点(你现在在这)
        ↓
  §1 为什么需要生命周期 ── 理解 OS 为什么要发明这套机制(三大根因)
        ↓
  §2 通用四件套契约 ── 看清"任何平台都遵循"的生命周期骨架(跨端原理)
        ↓
  §3-§6 各端实现 ── Android / iOS / React / Vue / Flutter / Compose(具体差异)
        ↓
  §7 横向对比矩阵 ── 不同平台生命周期的「翻译表」
        ↓
  §8 反模式 ── §0 事故只是冰山一角,还有 5 个常见埋雷
        ↓
  §9 现代演进 ── 响应式 + 协程怎么让生命周期"自动管理"
        ↓
  §10 收口 ── 跨端心智 + 与全卷呼应

# 1.为何需要生命周期

# 1.1 纯函数 vs 长生命

想象一个"没有生命周期"的世界:

    main() {
        Window w = createWindow();
        w.show();
        loop();  // 死循环处理事件
        w.destroy();
    }

这种「一进一出」的模型只适合:
    ✓ 命令行工具(grep、ls 一跑就退出)
    ✓ 简单脚本

但现代 UI 的本质是:
    ✗ 多窗口/多页面(Activity 栈、UINavigationController)
    ✗ 后台/前台切换(用户随时按 Home 键)
    ✗ 系统压力下被回收(内存不足)
    ✗ 配置变更(横竖屏、字体大小)
    ✗ 进程死亡又重启(用户回到 App)

没有生命周期就没有"组件何时该做什么"的契约——
开发者只能"猜"组件什么时候活、什么时候死、什么时候该释放资源。

# 1.2 三大根因

根因 1:所有权(Ownership)
    UI 组件不是"程序员创建的"——是"系统创建的"
        ── Activity 不是你 new 出来的,是系统 newInstance() 出来的
        ── UIViewController 在导航栈中由 UINavigationController 管理
        ── React 组件由 React Reconciler 决定何时挂载
    
    既然不是你创建的,你就不知道"它什么时候死"
        ── 系统必须给你"通知"——这就是 onDestroy / viewDidUnload / componentWillUnmount

根因 2:资源管理(Resource)
    每个组件都持有"系统资源":
        ── 内存(视图树、Bitmap)
        ── CPU(动画、定时器)
        ── 网络(连接、订阅)
        ── 传感器(GPS、加速度计)
        ── 文件句柄(数据库、文件流)
    
    资源必须"获取 → 使用 → 释放"配对,否则就泄漏
        ── 没有钩子,开发者无法在"恰当时机"释放

根因 3:可见性同步(Visibility)
    用户感知到的 UI 状态:可见 / 不可见 / 半透明
    必须与组件状态对齐:
        ── 不可见时不应耗电(停止动画、停止位置更新)
        ── 可见时立刻恢复(继续动画、刷新数据)
    
    可见性变化 = 用户行为 → 必须给开发者切入点

# 1.3 设计目标

任何生命周期机制都在追求 5 个目标:

  1. 可预测性(Predictability)
        ── 同样的用户操作 → 同样的回调序列
        ── 例:Android onCreate → onStart → onResume 永远是这个顺序

  2. 完备性(Completeness)
        ── 任何"组件可能进入的状态"都有回调
        ── 例:iOS 有 viewWillAppear / viewDidAppear / viewWillDisappear / viewDidDisappear,前后都覆盖

  3. 可配对性(Pairing)
        ── 资源"申请"的钩子一定有"释放"的钩子配对
        ── 例:onCreate ↔ onDestroy, onStart ↔ onStop, onResume ↔ onPause

  4. 可绑定性(Bindability)
        ── 异步任务能绑到组件生命周期上"自动取消"
        ── 例:lifecycleScope / viewLifecycleOwner / SwiftUI .onAppear

  5. 可恢复性(Recoverability)
        ── 组件死了能恢复状态
        ── 例:onSaveInstanceState / state restoration / SwiftUI @StateObject

# 2.生命周期四件套

本节是全篇骨架。剥离所有平台差异,任何生命周期机制都遵循这 4 个核心要素——理解了这 4 件套,从 Android 切到 iOS/React/Vue/Flutter 时不用重新学,只需要查"在新平台里叫什么"。

# 2.1 状态机

组件本质是「时间维度上的有限状态机」——5 个核心状态:

           ┌─────────────┐
           │ INITIALIZED │  (对象已创建,但还没"挂"到系统)
           └──────┬──────┘
                  │ onCreate / init / constructor
                  ▼
           ┌─────────────┐
           │   CREATED   │  (已挂到系统,但尚未显示)
           └──────┬──────┘
                  │ onStart / viewWillAppear / mounted
                  ▼
           ┌─────────────┐
           │   STARTED   │  (已可见,但尚未获得焦点)
           └──────┬──────┘
                  │ onResume / viewDidAppear / activated
                  ▼
           ┌─────────────┐
           │   RESUMED   │  (可见 + 可交互 = 最完整状态) ★
           └──────┬──────┘
                  │ onPause / viewWillDisappear / deactivated
                  ▼
                  ... 反向回到 STARTED → CREATED → DESTROYED
           ┌─────────────┐
           │  DESTROYED  │
           └─────────────┘

状态机的「跨端不变量」:

  1. 状态机是「单向序」——不能从 INITIALIZED 直接跳到 RESUMED,必须经过 CREATED、STARTED。
  2. 状态变化必有触发源——系统事件(按 Home)、用户行为(按返回)、配置变更(横竖屏)。
  3. 每个状态都有「资源消耗等级」——RESUMED 资源最重(动画、传感器全开),DESTROYED 资源为零。

# 2.2 钩子

钩子是「状态切换的回调函数」——给开发者一个"切入点"。

通用钩子模型:
  - 「进入状态前」钩子:will / before(让你做准备)
  - 「进入状态后」钩子:did / after(让你做收尾)
  - 「离开状态前」钩子:will + 反向动作
  - 「离开状态后」钩子:did + 反向动作

iOS 完整体现了「前后双钩子」:
    viewWillAppear (即将可见) → viewDidAppear (已经可见)
    viewWillDisappear (即将隐藏) → viewDidDisappear (已经隐藏)

Android 简化了「只有 did」:
    onCreate / onStart / onResume / onPause / onStop / onDestroy
    (没有 willStart / willPause——是设计权衡)

React Hooks 反过来——「不分前后,只看依赖」:
    useEffect(() => { ... return cleanup; }, [deps])
    (函数式范式:副作用与生命周期统一)

# 2.3 所有权

谁创建组件 → 谁管生命周期 → 谁负责销毁。

七端的「所有权链」:

  Android:
    Application → ActivityManagerService → Activity → Fragment → View
                  (AMS 管 Activity 生死,Activity 管 Fragment,Fragment 管 View)

  iOS:
    UIApplication → UIWindow → RootVC → UINavigationController → ChildVC
                    (UIWindow 持有 RootVC,导航栈 push/pop 决定子 VC 生死)

  React:
    ReactDOM.render → Root Fiber → Tree of Fibers
                      (Reconciler 决定 mount/update/unmount)

  Vue:
    createApp().mount() → Root Component → Child Components
                          (响应式系统驱动 mount/unmount)

  Flutter:
    runApp → Element Tree → State Tree
              (Element 是状态的所有者,Element 死则 State 死)

  Compose:
    setContent → Composition → @Composable Functions
                 (Composition 持有 RememberObservers)

  SwiftUI:
    @main App → Scene → View
              (SwiftUI Runtime 管理 view 的 init/deinit)

「所有权链」决定了「死亡传播」:
    父组件死 → 子组件全死 → 资源全释放
    
但「持有」会破坏这条链:
    Fragment 持有 Activity 引用 → Activity 想死却死不掉(§8.3 反模式)

# 2.4 可见性

可见性是「用户视角」,组件状态是「系统视角」,两者必须同步。

用户可见性的「四态」:
    1. 前台可见可交互:用户在看 + 在点 → 必须满血运行
    2. 前台可见不可交互:弹了 Dialog 在上面 → 暂停动画但保留状态
    3. 后台可见:分屏模式 / 画中画 → 减少 CPU 但保持渲染
    4. 完全不可见:被遮挡 / 切后台 → 释放重资源(相机、GPS)

系统通过「可见性钩子」把这四态翻译为生命周期:
    Android:
        onResume (1) ↔ onPause (2) ↔ onStop (4)
        Android Q+ 多窗口:(3) 也是 onResume,靠 isInPictureInPictureMode 区分

    iOS:
        viewDidAppear (1) ↔ viewWillDisappear (2) ↔ applicationDidEnterBackground (4)

    Web:
        document.visibilitychange / Page Visibility API
        IntersectionObserver(元素级可见性)

「可见性 ≠ 存活性」是关键认知:
    ✓ 切后台 ≠ 销毁,App 仍在内存中
    ✓ 不可见时仍可执行(后台音乐、下载、定时器)
    ✗ 但不可见时应"克制"——苹果会因后台耗电杀 App

# 2.5 跨端名词矩阵

任何生命周期事件在七大端的「同名异姓」字典——下次切端时先查这张表:

通用概念 Android Activity iOS UIViewController React Class React Hooks Vue Composition Flutter Compose SwiftUI
创建 onCreate init / loadView constructor useState 首次 setup initState remember { } 首次 init
首次挂载 onStart + onResume viewDidLoad componentDidMount useEffect(()=>{}, []) onMounted didChangeDependencies LaunchedEffect(Unit) .onAppear
即将可见 (无显式回调) viewWillAppear (无) (无) onBeforeMount (无) (无) .onAppear
已可见 onResume viewDidAppear (需自实现) (需 IntersectionObserver) (需自实现) (需自实现) (需自实现) .onAppear
更新前 (隐式) (隐式) componentWillUpdate (已废弃) (依赖变化时 useEffect) onBeforeUpdate didUpdateWidget LaunchedEffect(key) .onChange(of:)
更新后 (隐式) (隐式) componentDidUpdate useEffect(()=>{}, [dep]) onUpdated didUpdateWidget LaunchedEffect(key) .onChange(of:)
即将不可见 onPause viewWillDisappear (需自实现) (需 IntersectionObserver) (需自实现) deactivate (需自实现) .onDisappear
已不可见 onStop viewDidDisappear (需自实现) (需自实现) (需自实现) (需自实现) (需自实现) .onDisappear
销毁前 onDestroy deinit componentWillUnmount useEffect cleanup onBeforeUnmount dispose DisposableEffect.onDispose .onDisappear + deinit
状态保存 onSaveInstanceState encodeRestorableState (需自实现) (需自实现) (需自实现) Storage rememberSaveable @SceneStorage
状态恢复 onRestoreInstanceState decodeRestorableState (需自实现) (需自实现) (需自实现) (需自实现) rememberSaveable @SceneStorage
配置变更 onConfigurationChanged traitCollectionDidChange (需自实现) (需 useEffect 监听) (需 watch) didChangeMetrics (状态自动更新) (状态自动更新)
进入后台 onStop + ProcessLifecycleOwner applicationDidEnterBackground visibilitychange visibilitychange visibilitychange AppLifecycleState.paused ScenePhase.background ScenePhase.background
资源释放钩子 onDestroy deinit (ARC) componentWillUnmount useEffect cleanup onBeforeUnmount dispose DisposableEffect.onDispose .onDisappear
作用域绑定 lifecycleScope / viewLifecycleOwner Combine cancellables AbortController useEffect cleanup effectScope mounted flag rememberCoroutineScope .task modifier

「四件套契约」的跨端不变量:

  1. 状态机的「五状态」是普世的——任何平台都能映射到 INIT → CREATED → STARTED → RESUMED → DESTROYED。
  2. 钩子的「数量」反映了平台的"心智成本"——iOS 5 个、Android 13 个、React Hooks 1 个,越少越简单但灵活性越低。
  3. 所有权链 = 树形结构——所有平台的组件都是树,父死则子全死,"打破链"就是反模式。
  4. 可见性钩子总比生命周期钩子细——可见性变化频繁(每次按 Home),生命周期变化稀疏(每次新建/销毁)。
  5. 现代框架都在向「函数式生命周期」收敛——React Hooks/Compose/SwiftUI 把"出生/死亡/更新"全部归约到「副作用」一个概念上。

给所有应用开发者的总记忆:

不论你在做 Android、iOS、Web、Flutter、Compose、SwiftUI——任何组件生命周期都是「四件套:状态机 / 钩子 / 所有权 / 可见性」的不同组合。下次遇到「为什么我的代码崩溃 / 内存泄漏 / 不刷新」时你能精准说出"问题在哪一件套":是状态机理解错(在错误状态做了错误事,§0 案例)/ 钩子用错(onPause 做重活,§8.1)/ 所有权弄丢(持有反向引用,§8.3)/ 可见性没监听(后台还在耗电),而不是"框架的 bug"。


# 3.Android 生命周期

# 3.1 Activity 七大回调

完整的 Activity 状态机(含异常情况):

      [不存在]
         │
         │ startActivity()
         ▼
      onCreate()  ← 创建视图、初始化数据、绑定 ViewModel
         │
         ▼
      onStart()   ← 可见但不可交互(如部分遮挡)
         │
         ▼
      onResume()  ← 完全可见可交互 ★(最完整状态)
         │
         │ ←─────────── 用户按 Home / 弹窗
         ▼
      onPause()   ← 失去焦点(弹 Dialog、跳转、按 Home)
         │
         │ ←─────────── 应在 100ms 内返回(否则下个 Activity 等待)
         ▼
      onStop()    ← 完全不可见
         │
         ▼
      onDestroy() ← 销毁
         │
         ▼
      [被回收]

异常路径:
    onPause → onResume (短暂失焦后恢复,如弹完 Dialog)
    onStop  → onRestart → onStart → onResume(从最近任务恢复)

七大回调的「资源管理职责」:

回调 时机 该做什么 不该做什么
onCreate 仅一次,组件诞生 初始化视图、绑定 ViewModel、注册系统监听 不要发网络请求(应该让 ViewModel 做)
onStart 可见前 注册广播、订阅数据流 不要做耗时操作
onResume 可交互时 启动动画、恢复 Camera/GPS、启动定时器 不要做 IO
onPause 失去焦点 暂停动画、传感器;异步保存草稿 严禁同步 IO/网络(100ms 限制)
onStop 完全不可见 释放重资源(Camera)、停止定时器 不要假设它一定会被调用(系统可直接杀进程)
onDestroy 销毁前 解绑监听、释放对象引用 不要依赖它做关键存盘(可能不被调用)
onRestart 从 Stop 恢复 刷新 UI(数据可能变化) 不要重新初始化(保留原状态)

关键认知 1:onPause 的 100ms 限制

为什么 onPause 严禁同步 IO?

  startActivity B:
    A.onPause() → [必须返回] → B.onCreate() → B.onStart() → B.onResume()
    
  如果 A.onPause() 阻塞 5 秒:
    → B 的所有回调全部延迟 5 秒
    → 用户感知到「点击后 5 秒才看到新页面」
    → 误以为是 B 卡,其实是 A 在拖后腿

关键认知 2:onDestroy 不一定被调用

被回收的三种情况:
    1. 正常销毁:finish() / 用户按返回 → onDestroy 被调用 ✓
    2. 系统内存压力杀 Activity:onStop 后直接杀进程 → onDestroy 不调用 ✗
    3. 用户在最近任务列表划掉:直接杀进程 → onDestroy 不调用 ✗

正确做法:
    ✓ 关键存盘放 onPause(异步)
    ✓ 资源解绑可以放 onDestroy(如果没调用,进程都死了无所谓)
    ✗ 不要把"必须执行"的事放 onDestroy

# 3.2 Fragment 13 回调

Fragment 比 Activity 多 6 个回调,本质是「视图与 Fragment 解耦」:

      onAttach(context)          ← Fragment 关联到 Activity
         │
         ▼
      onCreate(savedState)        ← Fragment 实例创建(不含视图)
         │
         ▼
      onCreateView()              ← ★ 创建视图(独立于 onCreate)
         │
         ▼
      onViewCreated()             ← 视图创建完成,可绑定数据
         │
         ▼
      onStart() → onResume()      ← 与 Activity 同步
         │
         ▼
      onPause() → onStop()        ← 与 Activity 同步
         │
         ▼
      onDestroyView()             ← ★ 销毁视图(但 Fragment 还在!)
         │
         ▼
      onDestroy()                 ← Fragment 实例销毁
         │
         ▼
      onDetach()                  ← 从 Activity 解关联

「Fragment 和 View 不同寿」的设计哲学:

为什么 Fragment 要区分 onCreate 和 onCreateView?

  场景:ViewPager 中的 Fragment
        - 用户滑动:当前 Fragment 视图销毁(onDestroyView),但 Fragment 实例保留
        - 滑回来:重建视图(onCreateView),Fragment 实例不变
        - 好处:保留状态 + 节省内存

  这就引出 Fragment 开发的「黄金法则」:

  ★ 与 Fragment 共寿 → 用 lifecycleScope / lifecycle
  ★ 与 View 共寿     → 用 viewLifecycleOwner / viewLifecycleOwner.lifecycleScope

  错误:
    binding 在 onCreateView 创建 → 在 onDestroy 释放
    bug:onDestroyView 后 binding 持有的视图已死,但还没置 null → 内存泄漏

  正确:
    binding 在 onCreateView 创建 → 在 onDestroyView 置 null

# 3.3 配置变更与恢复

Android 生命周期最难的部分——「组件死了但状态要活」。

三种「销毁」的本质区别:

  ① 配置变更(横竖屏、字体大小、语言)
      默认行为:Activity 完全重建(onDestroy → onCreate)
      恢复方式:onSaveInstanceState(Bundle) 保存 → onCreate(Bundle) 恢复
      内存中:原 Activity 销毁,新 Activity 创建
      
  ② 系统内存压力(用户切到别的 App,内存不足)
      行为:进程被杀,但「任务栈」保留
      恢复方式:用户回到 App → 系统重建 Activity 栈,调 onCreate 传入 Bundle
      关键:onSaveInstanceState 在 onPause/onStop 后调用,保 Bundle
      
  ③ 用户手动退出(划掉最近任务)
      行为:进程被杀 + 任务栈清空
      恢复方式:不恢复,重新启动 App
      关键:所有未持久化数据都丢

保存机制对比:

  onSaveInstanceState(Bundle) 适合:
      ✓ 临时 UI 状态(输入框文字、滚动位置、Tab 选中)
      ✓ 小数据量(< 1MB,Bundle 通过 Binder 传输)
      ✗ 不适合"长期数据"(应该入库)

  ViewModel 适合:
      ✓ 跨配置变更保留数据(横竖屏不重建)
      ✗ 不跨进程死亡保留(进程死则 ViewModel 死)

  SavedStateHandle (ViewModel 1.2+):
      ✓ ViewModel + onSaveInstanceState 的"集大成"
      ✓ 跨配置变更 + 跨进程死亡都能恢复

进程死亡恢复的「黄金组合」:

class DetailViewModel(
    private val savedStateHandle: SavedStateHandle  // 自动持久化
) : ViewModel() {
    
    // 用户的输入草稿——进程死了也要恢复
    val draft: MutableLiveData<String> = savedStateHandle.getLiveData("draft")
    
    // 加载的商品详情——重启时重新加载即可
    val product: LiveData<Product> = liveData {
        emit(repo.loadProduct(savedStateHandle["productId"] ?: ""))
    }
}

# 3.4 ViewModel 解耦

ViewModel 解决了 Android 生命周期的「世纪难题」:

  问题:如何让数据"超越"Activity 的生命周期?
  
  传统方案的痛点:
    - static 字段 → 内存泄漏,多 Activity 共享乱套
    - onSaveInstanceState → 只能存简单类型
    - SharedPreferences → IO 慢,不适合复杂对象
    - 单例 → 跨 Activity 生命周期混乱
  
  ViewModel 的设计:
    - 范围(Scope)= ViewModelStoreOwner(Activity / Fragment / NavGraph)
    - 生命周期 = Scope 完全销毁时(不是配置变更)
    - 内存中由 ViewModelStore 持有,配置变更时 Activity 重建但 ViewModel 保留

  ViewModelStore 的"魔法":
    ① Activity#onRetainNonConfigurationInstance() ← 配置变更前调用
    ② 系统保存 ViewModelStore 到一个特殊位置
    ③ Activity 重建后从同一位置恢复
    ④ 新 Activity 通过 ViewModelProvider 拿到同一个 ViewModel 实例

# 4.iOS 生命周期

# 4.1 UIViewController 回调

iOS UIViewController 的生命周期比 Android 简洁得多——
原因是 iOS 用「ARC + 引用计数」管理对象,不需要 onStop/onRestart 那种复杂状态。

      [init]
         │
         ▼
      loadView         ← 加载 view 属性(一般不用重写)
         │
         ▼
      viewDidLoad      ← ★ 视图层级加载完成(仅一次,等价 onCreate)
         │
         ▼
      viewWillAppear   ← 即将显示(每次都调,含 push/pop 回来)
         │
         ▼
      viewIsAppearing  ← (iOS 13+) 已开始显示但未结束动画
         │
         ▼
      viewDidAppear    ← 已完全显示 ★
         │
         │ ←─────── 用户操作
         ▼
      viewWillDisappear  ← 即将隐藏
         │
         ▼
      viewDidDisappear   ← 已隐藏
         │
         ▼
      deinit           ← ARC 引用计数归零时调用(无显式钩子)

iOS 与 Android 的「设计哲学差异」:

维度 Android iOS
回调数量 13 个(Fragment) 5 个(UIViewController)
资源释放 onDestroy(不一定被调) deinit(ARC 自动)
状态保存 onSaveInstanceState(显式) encodeRestorableState(默认开启)
配置变更 默认重建(要存状态) 不重建,传 trait 变更
导航栈 Activity 栈(系统管) UINavigationController(开发者管)
设计哲学 「系统主导」(被回收是常态) 「开发者主导」(ARC 自动管理)

# 4.2 ARC 与 deinit

iOS 的"隐式生命周期"——ARC 决定何时释放对象。

    init → 引用计数+1
    被强引用 → 引用计数+1
    引用释放 → 引用计数-1
    引用计数 = 0 → deinit 被调用

经典陷阱:循环引用(Retain Cycle)

    class ViewController: UIViewController {
        var timer: Timer?
        
        override func viewDidLoad() {
            // ❌ 循环引用:self → timer → closure → self
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
                self.update()
            }
        }
    }
    // 结果:deinit 永远不调用 → 内存泄漏

正确做法:

    timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
        self?.update()  // ✓ weak 打破循环
    }

iOS 资源释放的「最佳实践」:

class CameraViewController: UIViewController {
    var captureSession: AVCaptureSession?
    var cancellables = Set<AnyCancellable>()  // Combine 订阅集合
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        startCamera()
        subscribeToData()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        stopCamera()  // ✓ 不可见前停相机
    }
    
    deinit {
        cancellables.removeAll()  // 显式取消(其实 ARC 会自动做)
        print("VC dealloc")  // 用来验证没循环引用
    }
}

# 4.3 SwiftUI 声明式

SwiftUI 完全颠覆了"命令式生命周期"——
View 不再是"对象",而是"描述"(Description)。

    struct ContentView: View {
        @State var count = 0
        
        var body: some View {
            VStack {
                Text("\(count)")
                Button("Tap") { count += 1 }
            }
            .onAppear { print("appear") }      // ★ 等价 viewDidAppear
            .onDisappear { print("disappear") } // ★ 等价 viewDidDisappear
            .task { await loadData() }          // ★ 绑定生命周期的异步任务
            .onChange(of: count) { newValue in  // ★ 状态变化钩子
                print("changed: \(newValue)")
            }
        }
    }

SwiftUI 的"心智革命":
    ✗ 不再问"组件什么时候创建"——SwiftUI 频繁创建/销毁 View 描述
    ✓ 只问"状态什么时候变化"——@State / @StateObject / @ObservedObject

@StateObject vs @ObservedObject:
    @StateObject:当前 View 持有所有权,View 在则它在
    @ObservedObject:父 View 传入,所有权在父
    
    错误用法:
        struct ParentView: View {
            var body: some View {
                ChildView()
            }
        }
        struct ChildView: View {
            @ObservedObject var vm = ViewModel()  // ❌ 每次重渲染都新建!
        }
    
    正确用法:
        struct ChildView: View {
            @StateObject var vm = ViewModel()  // ✓ View 持有所有权,只创建一次
        }

# 5.前端生命周期

# 5.1 React Class 回调

React 16+ 的 Class 组件生命周期分三阶段:

  挂载阶段(Mount):
      constructor(props)
          ↓
      getDerivedStateFromProps(props, state)  // 静态方法
          ↓
      render()
          ↓
      componentDidMount()  // ★ 等价 onResume / viewDidAppear
  
  更新阶段(Update):
      getDerivedStateFromProps
          ↓
      shouldComponentUpdate(nextProps, nextState)  // 性能优化点
          ↓
      render()
          ↓
      getSnapshotBeforeUpdate(prevProps, prevState)  // DOM 变更前
          ↓
      componentDidUpdate(prevProps, prevState, snapshot)
  
  卸载阶段(Unmount):
      componentWillUnmount()  // ★ 等价 onDestroy / deinit

  异常阶段:
      static getDerivedStateFromError
      componentDidCatch  // 错误边界

React 已废弃的"Will" 系列:

componentWillMount      // React 16.3 废弃
componentWillReceiveProps  // React 16.3 废弃
componentWillUpdate     // React 16.3 废弃

为什么废弃?
    Fiber 架构允许"中断渲染"——这些 Will 钩子可能被调用多次
    导致开发者在 willMount 发请求时被调用 N 次 → 资源浪费
    
React 团队的态度:
    ✓ 用 componentDidMount(保证只调一次)
    ✓ 用 getDerivedStateFromProps(纯函数,没副作用)
    ✓ 用 useEffect(Hooks 范式)

# 5.2 React useEffect

function MyComponent({ id }) {
  const [data, setData] = useState(null);
  
  // 等价 componentDidMount + componentWillUnmount
  useEffect(() => {
    const subscription = api.subscribe(id, setData);
    return () => subscription.unsubscribe();  // ★ cleanup
  }, []);
  
  // 等价 componentDidUpdate(仅当 id 变化时)
  useEffect(() => {
    fetchData(id).then(setData);
  }, [id]);
  
  return <div>{data}</div>;
}

Hooks 的"心智模型":

  1. useEffect = 「副作用」 = 「与外部世界的交互」
  2. 依赖数组 = 「什么变化时重新跑」
  3. cleanup 函数 = 「上次副作用的回收」

经典陷阱:闭包捕获过期状态(Stale Closure)

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1);  // ❌ count 永远是 0(闭包捕获)
    }, 1000);
    return () => clearInterval(timer);
  }, []);  // ❌ 依赖空 → 闭包捕获初始值
}

// 正确做法:
setCount(c => c + 1);  // 函数式更新,不依赖外部 count

# 5.3 Vue Options 钩子

Vue 2/3 Options API 提供 8 个生命周期钩子:

      beforeCreate    ← data/methods 还没初始化
          ↓
      created         ← data/methods 已初始化(DOM 还没挂)
          ↓
      beforeMount     ← 即将挂载到 DOM
          ↓
      mounted         ← ★ DOM 已挂载(最常用)
          ↓
      (响应式数据变化触发更新)
      beforeUpdate    ← 即将重新渲染
          ↓
      updated         ← 已重新渲染
          ↓
      beforeUnmount   ← 即将销毁(Vue 3,Vue 2 叫 beforeDestroy)
          ↓
      unmounted       ← 已销毁(Vue 2 叫 destroyed)

  额外钩子:
      activated       ← keep-alive 缓存组件激活
      deactivated     ← keep-alive 缓存组件失活
      errorCaptured   ← 错误捕获

# 5.4 Vue Composition API

import { onMounted, onBeforeUnmount, ref, watch } from 'vue'

export default {
  setup() {
    const count = ref(0)
    
    onMounted(() => {
      console.log('mounted')
      startTimer()
    })
    
    onBeforeUnmount(() => {
      console.log('about to unmount')
      stopTimer()
    })
    
    // 等价 useEffect with deps
    watch(count, (newVal, oldVal) => {
      console.log(`changed: ${oldVal} → ${newVal}`)
    })
    
    return { count }
  }
}

Vue Composition vs React Hooks 的根本差异:

维度 Vue Composition React Hooks
setup 执行次数 仅 1 次 每次渲染都跑
状态依赖 响应式系统(Proxy)自动追踪 显式依赖数组
更新触发 响应式数据变化 setState 调用
闭包陷阱 无 有(Stale Closure)
逻辑复用 composable 函数 custom hooks
心智 接近 OOP 接近纯函数

# 6.函数式生命周期

# 6.1 Flutter Stateful 11 阶段

Flutter StatefulWidget 把"组件"拆成两个对象:
    - Widget(不可变,频繁创建)
    - State(可变,持久保存)

  完整生命周期:

      createState()          ← 创建 State 对象(仅一次)
          ↓
      initState()            ← ★ State 初始化(等价 onCreate)
          ↓
      didChangeDependencies  ← 依赖变化(InheritedWidget 变了)
          ↓
      build()                ← 构建 Widget 树
          ↓
      (父 Widget 重建时)
      didUpdateWidget(old)   ← Widget 配置更新
          ↓
      build()
          ↓
      (从树中移除)
      deactivate()           ← 暂时从树中移除(可能重新插入)
          ↓
      dispose()              ← ★ 最终销毁(等价 onDestroy)

Flutter 的"性能心智":
    ✗ Widget 频繁重建(每次 setState 整棵子树)
    ✓ State 持久保留(initState/dispose 只调一次)
    → 重对象(如 AnimationController)放 State,不要放 Widget

# 6.2 Compose remember

@Composable
fun MyScreen(id: String) {
    // 等价 useState
    var count by remember { mutableStateOf(0) }
    
    // 等价 componentDidMount(仅首次执行)
    LaunchedEffect(Unit) {
        loadInitialData()
    }
    
    // 等价 useEffect with deps(id 变化时重新执行)
    LaunchedEffect(id) {
        loadProductDetails(id)
    }
    
    // 等价 useEffect with cleanup(最完整)
    DisposableEffect(Unit) {
        val listener = registerListener()
        onDispose {
            unregisterListener(listener)  // ★ 离开 Composition 时执行
        }
    }
    
    // 跨进程死亡保存状态
    var draft by rememberSaveable { mutableStateOf("") }
}

Compose 副作用 API 全家桶:

API 作用 等价于
LaunchedEffect(key) 协程作用域绑定 Composition useEffect with deps
DisposableEffect(key) 需要清理的副作用 useEffect + cleanup
SideEffect 每次成功重组都执行 useEffect 无 deps
rememberCoroutineScope 在事件回调里启动协程 useRef + AbortController
rememberUpdatedState 解决闭包过期问题 useRef
produceState 把异步流转 State 自定义 hook
derivedStateOf 派生状态(避免不必要重组) useMemo
snapshotFlow State → Flow (无对应)

# 6.3 函数式 vs 命令式

两种范式的根本差异:

  命令式(Android Activity / iOS UIViewController / Vue Options):
      开发者写「在什么时机做什么」
      onCreate { create view }
      onResume { start sensor }
      onPause  { stop sensor }
      onDestroy { release }

  函数式(React Hooks / Compose / SwiftUI):
      开发者写「什么状态对应什么副作用」
      useEffect(() => {
          const handle = startSensor()
          return () => stopSensor(handle)
      }, [isActive])

  心智差异:
      命令式:思考"时间线" → 在每个时间点做对的事
      函数式:思考"状态依赖" → 描述状态如何决定副作用,框架决定何时执行

  优势对比:
      命令式 ✓ 直观,易理解("按顺序")
      命令式 ✗ 容易遗漏配对(注册了忘解绑)
      函数式 ✓ 配对自动(cleanup 强制写)
      函数式 ✗ 心智弯曲,调试难(闭包陷阱、依赖数组)

# 7.横向对比矩阵

# 7.1 能力对齐表

维度 \ 平台 Android iOS React Hooks Vue Composition Flutter Compose SwiftUI
回调数量 13 (Fragment) 5 1 (useEffect) 7 11 6 (Effect 系列) 4 (生命周期 modifier)
范式 命令式 命令式 函数式 半函数式 命令式(State 内) 函数式 函数式
创建钩子 onCreate viewDidLoad useState 初始化 setup initState remember 首次 init
销毁钩子 onDestroy deinit (ARC) useEffect cleanup onBeforeUnmount dispose DisposableEffect.onDispose (ARC + onDisappear)
可见性钩子 onResume/onPause viewDidAppear/Disappear (需自实现) (需自实现) (需自实现) (需自实现) .onAppear/.onDisappear
状态保存 onSaveInstanceState + ViewModel encodeRestorableState (需自实现) (需 store) Storage 库 rememberSaveable @SceneStorage
作用域绑定 lifecycleScope Combine cancellables useEffect cleanup effectScope mounted flag rememberCoroutineScope .task modifier
跨配置变更存活 ViewModel 自动(VC 不重建) 自动(无重建) 自动 State 保留 rememberSaveable @StateObject
心智成本 高(13 个回调) 中(5 个) 低(1 个)但有陷阱 中(7 个) 中(11 个) 低(5 个 Effect) 极低(声明式)
典型 bug Fragment 持有 Activity 闭包循环引用 Stale closure watch 死循环 setState after dispose rememberSaveable 不可序列化 @ObservedObject 用错

# 7.2 设计哲学站位

                      命令式
                        │
        Android ●       │
            (13 回调)   │
                        │      ● iOS UIVC
                        │       (5 回调)
                        │
                        │● Flutter
                        │ (11 阶段)
            ─────────────┼─────────────  范式光谱
                        │
                        │● Vue Composition
                        │ (7 钩子)
                        │
              Compose ● │● React Hooks
                  (6)   │   (1 个 useEffect)
                        │
                  SwiftUI ●(4 modifier)
                        │
                      函数式

横轴:命令式 ←→ 函数式
纵轴:回调数量(多 ←→ 少)

「演化方向」:所有平台都在从"命令式 + 多回调"向"函数式 + 少 API"演化。
    Android Activity → Compose
    iOS UIKit → SwiftUI
    Vue Options → Composition
    React Class → Hooks

# 8.反模式与陷阱

§0 案例只是冰山一角,下面是 5 个最常见、最致命的反模式。这 5 个反模式覆盖了 90% 的生命周期事故。

# 8.1 onPause 耗时

症状:用户感知到「按返回慢半拍」、概率 ANR、Activity 切换卡顿。

错误代码:

override fun onPause() {
    super.onPause()
    // ❌ 同步写大文件
    File(filesDir, "draft.txt").writeText(largeContent)
    // ❌ 同步网络请求
    api.uploadDraft(draft).execute()
    // ❌ SharedPreferences.commit()
    prefs.edit().putString("k", v).commit()
}

为什么是反模式:onPause 必须 100ms 内返回(系统硬性要求)——下一个 Activity 的所有生命周期都在等它。

正确做法:

override fun onPause() {
    super.onPause()
    // ✓ 异步存储
    lifecycleScope.launch(Dispatchers.IO) {
        File(filesDir, "draft.txt").writeText(largeContent)
    }
    // ✓ apply 替代 commit(异步)
    prefs.edit().putString("k", v).apply()
    // ✓ 仅停止"必须立即停"的(动画、传感器)
    animation.pause()
    locationManager.removeUpdates(this)
}

# 8.2 异步回调悬垂

症状:IllegalStateException: Fragment not attached、NullPointerException on view、内存泄漏。

错误代码:

override fun onCreateView(...) {
    api.fetchData { data ->
        // ❌ 5 秒后回调,Fragment 可能已 detach
        textView.text = data.title  // 崩溃:textView 已被回收
    }
}

正确做法(三种方案):

// 方案 1:viewLifecycleOwner.lifecycleScope(推荐)
viewLifecycleOwner.lifecycleScope.launch {
    val data = api.fetchData()
    if (isAdded && view != null) {  // 双保险
        textView.text = data.title
    }
}

// 方案 2:LiveData + observe(viewLifecycleOwner)
viewModel.data.observe(viewLifecycleOwner) { data ->
    textView.text = data.title  // Lifecycle 自动管理,组件死则不回调
}

// 方案 3:Flow.flowWithLifecycle
lifecycleScope.launch {
    viewModel.dataFlow
        .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
        .collect { textView.text = it.title }
}

# 8.3 Fragment 强引用

症状:横屏后内存翻倍、LeakCanary 报警、配置变更后旧 Activity 不释放。

错误代码:

class MyFragment : Fragment() {
    var activity: MainActivity? = null  // ❌ 强引用 Activity
    
    override fun onAttach(context: Context) {
        super.onAttach(context)
        activity = context as MainActivity  // ❌ 持有 Activity 引用
    }
    // 配置变更:Activity 销毁,但 Fragment 还持有它 → 内存泄漏
}

正确做法:

// ✓ 用 requireActivity() / activity,而不是缓存
override fun doSomething() {
    val act = activity ?: return  // 用临时变量
    act.someMethod()
}

// ✓ 跨 Fragment-Activity 通信用接口 + ViewModel
class MyFragment : Fragment() {
    private val sharedVM: SharedViewModel by activityViewModels()
    // 用 ViewModel 通信,而不是直接持有 Activity
}

# 8.4 闭包循环引用

症状(iOS):deinit 永远不调用、Instruments 显示内存持续增长。

错误代码(Swift):

class ViewController: UIViewController {
    var dataLoader: DataLoader?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // ❌ 循环引用:self → dataLoader → closure → self
        dataLoader = DataLoader()
        dataLoader?.onComplete = { data in
            self.updateUI(data)  // self 被强引用
        }
    }
}

正确做法:

// ✓ weak self 打破循环
dataLoader?.onComplete = { [weak self] data in
    self?.updateUI(data)
}

// ✓ 如果一定不能 nil(如动画完成回调)
dataLoader?.onComplete = { [unowned self] data in
    self.updateUI(data)
}

// ✓ Combine 自动管理
dataLoader.publisher
    .sink { [weak self] data in self?.updateUI(data) }
    .store(in: &cancellables)  // VC 死则订阅自动取消

# 8.5 忘记反注册

症状:通知触发死对象、广播接收器在后台仍触发、EventBus 收到事件后崩溃。

错误代码:

// Android
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    EventBus.register(this)        // ❌ 没在 onDestroy 反注册
    sensorManager.registerListener(this, sensor, RATE)  // ❌
}

正确做法:

// 方案 1:配对回调(笨办法)
override fun onCreate(...) {
    EventBus.register(this)
}
override fun onDestroy() {
    EventBus.unregister(this)
    super.onDestroy()
}

// 方案 2:Lifecycle-aware Observer(推荐)
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ✓ 自动绑定生命周期,无需手动反注册
        lifecycle.addObserver(MyLocationObserver(this))
    }
}

class MyLocationObserver(val context: Context) : DefaultLifecycleObserver {
    override fun onStart(owner: LifecycleOwner) { startLocation() }
    override fun onStop(owner: LifecycleOwner) { stopLocation() }
}

// 方案 3:协程作用域(Flow/StateFlow)
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        eventBus.eventFlow.collect { handleEvent(it) }
        // STARTED → STOPPED 时自动取消
    }
}

五大反模式的「共同根因」:

所有反模式都源于「混淆了对象生命周期 和 业务生命周期」:

  对象生命周期:内存中 new 到 GC/dealloc 的时间
  业务生命周期:用户感知到"这页存在"的时间

  正确的心智:
    ✗ 不要假设"对象还在 → 一切都正常"
    ✓ 要假设"对象可能在 → 但视图已死 / 上下文已变 / 数据已过期"

  解决思路:
    1. 任何"绑定关系"都必须有"自动解绑"机制
    2. 任何"异步回调"都必须有"组件存活检查"
    3. 任何"耗时操作"都必须从生命周期回调里搬走

# 9.现代演进

2018-2024 年,生命周期管理领域出现了**「响应式 + 协程 + 函数式」**三件套革命——把"手动管钩子"变成"框架自动管"。

# 9.1 LifecycleOwner 绑定

传统问题:手动在 onStart/onStop 中订阅/取消订阅,重复且易错。

现代方案(Jetpack):

class MyFragment : Fragment() {
    private val viewModel: MyViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // ✓ LiveData 自动感知 Lifecycle
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // 仅在 STARTED 及以上状态时调用
            // 视图销毁则自动停止观察
            updateUI(user)
        }
        
        // ✓ Flow + repeatOnLifecycle
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    render(state)
                }
            }
        }
    }
}

LifecycleOwner 的核心契约:

LifecycleOwner = "拥有生命周期的对象"
    Activity / Fragment / ViewModel / 自定义类都可以实现

LiveData/Flow 的"魔法":
    1. 订阅时传入 LifecycleOwner
    2. 内部监听 owner.lifecycle.addObserver
    3. ON_START → 开始投递数据
    4. ON_STOP  → 停止投递(数据缓存最新值)
    5. ON_DESTROY → 自动反订阅

→ 开发者再也不用写 onStart/onStop 配对

# 9.2 协程作用域

class MyViewModel : ViewModel() {
    init {
        // viewModelScope:ViewModel 销毁时自动取消所有协程
        viewModelScope.launch {
            val data = repository.fetchData()
            _state.value = data
        }
    }
}

class MyFragment : Fragment() {
    override fun onViewCreated(...) {
        // viewLifecycleOwner.lifecycleScope:View 销毁时自动取消
        viewLifecycleOwner.lifecycleScope.launch {
            val data = api.fetchData()
            textView.text = data.title
        }
    }
}

三种 Scope 的「生命周期对齐」:

Scope 持有方 取消时机 用途
lifecycleScope (Activity/Fragment) Activity/Fragment onDestroy 与组件共寿
viewLifecycleOwner.lifecycleScope Fragment 的 View onDestroyView 与视图共寿(推荐)
viewModelScope ViewModel onCleared 跨配置变更存活
GlobalScope 应用进程 进程死亡 危险(不推荐)

# 9.3 Combine 自动取消

class ViewModel: ObservableObject {
    @Published var data: Data?
    private var cancellables = Set<AnyCancellable>()
    
    func loadData() {
        api.fetchData()
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { _ in },
                  receiveValue: { [weak self] data in
                self?.data = data
            })
            .store(in: &cancellables)  // ★ 关键:与 ViewModel 共寿
    }
    
    // deinit 时 cancellables 自动取消所有订阅
}

// SwiftUI 的 .task modifier 更进一步:
struct ContentView: View {
    var body: some View {
        Text("Loading...")
            .task {
                // 自动绑定 View 生命周期
                await loadData()
                // View 消失则自动取消
            }
    }
}

# 9.4 副作用与生命周期

现代框架的"终极形态"——把生命周期完全隐藏在「副作用钩子」里:

  React:    useEffect(fn, deps)
  Compose:  LaunchedEffect(key) { fn() } / DisposableEffect(key) { onDispose { } }
  SwiftUI:  .task(id:) { } / .onChange(of:) { } / .onAppear { }

  开发者不再思考:
      ✗ "组件什么时候创建"
      ✗ "组件什么时候销毁"
      ✗ "组件什么时候更新"

  只思考:
      ✓ "依赖了什么状态"
      ✓ "状态变了要做什么"
      ✓ "做完要怎么清理"

→ 这是「程序员认知负担最低」的生命周期范式
→ 也是为什么 2024 年所有主流框架都在向这个方向收敛

演进总结:从命令式到响应式

第一代(1995-2010):传统命令式
   ─ MFC、UIKit、Android Activity
   ─ 钩子很多,开发者全靠手记

第二代(2008-2018):观察者模式
   ─ Android Lifecycle Library、RxJava、Combine
   ─ 用观察者模式自动管订阅生命周期

第三代(2018-2024):响应式 + 协程
   ─ Kotlin Coroutines、LiveData、StateFlow
   ─ 用作用域 + 取消机制自动管异步任务

第四代(2020+):函数式声明式
   ─ Compose、SwiftUI、React Hooks
   ─ 副作用 = 生命周期,框架完全自动管理

# 10.心智与哲学

# 10.1 跨端术语对照

任何生命周期开发者必备的「同名异姓」字典:

通用概念 Android iOS UIKit SwiftUI React Vue Flutter Compose
诞生 onCreate init / loadView init constructor beforeCreate / created createState / initState remember 首次
挂载完成 onStart viewDidLoad onAppear 首次 componentDidMount mounted (initState 后) LaunchedEffect(Unit)
可交互 onResume viewDidAppear onAppear (隐式) (隐式) (build 后) (隐式)
失焦 onPause viewWillDisappear onDisappear (需自实现) (需自实现) deactivate (需自实现)
不可见 onStop viewDidDisappear onDisappear (需自实现) (需自实现) deactivate (需自实现)
销毁 onDestroy deinit onDisappear + ARC componentWillUnmount unmounted dispose DisposableEffect.onDispose
更新 (隐式 setContentView) (隐式) onChange / body 重算 componentDidUpdate updated didUpdateWidget (重组)
状态保存 onSaveInstanceState encodeRestorableState @SceneStorage (需自实现) (需 store) (需自实现) rememberSaveable
作用域绑定 lifecycleScope cancellables .task useEffect cleanup effectScope mounted flag rememberCoroutineScope
可见性监听 LifecycleObserver viewDidAppear/Disappear onAppear/Disappear IntersectionObserver watch + visibility VisibilityDetector (需自实现)
进入后台 ProcessLifecycleOwner applicationDidEnterBackground ScenePhase.background visibilitychange visibilitychange AppLifecycleState ProcessLifecycleOwner
配置变更 onConfigurationChanged traitCollectionDidChange (自动) (需手动) (需手动) didChangeMetrics (自动)
典型 bug Fragment 持 Activity 循环引用 @ObservedObject 用错 Stale closure watch 死循环 setState after dispose rememberSaveable 不可序列化
性能埋雷 onPause 做 IO viewDidLoad 做网络 body 做副作用 useEffect 缺 deps watch 触发 watch initState 做重活 SideEffect 滥用

把这张表贴在 IDE 旁边——切换平台时不再需要"重新学生命周期",只需要查"在新平台里它叫什么"。

# 10.2 本卷章节呼应

5.1 窗口核心设计思想       ─→ Activity/UIViewController 是"窗口的内容容器"
                              窗口生命周期 ⊃ 组件生命周期
                              addView/removeView 触发 onAttached/onDetached

5.2 视图加载渲染设计       ─→ onCreateView 是视图加载的入口
                              视图树挂载/卸载 = onViewCreated/onDestroyView
                              setContentView 触发整棵 View 树的生命周期

5.3 图形渲染管线原理       ─→ onResume 后才开始 Choreographer 渲染
                              onPause 后停止 invalidate
                              生命周期决定"什么时候 SurfaceFlinger 合成"

5.4 手势事件设计灵魂       ─→ 仅 RESUMED 状态才接收触摸事件
                              onPause 后 InputDispatcher 不再投递
                              生命周期是事件分发的"开关"

5.5 消息机制设计思想       ─→ Handler/runOnUiThread 必须在 STARTED+ 状态执行
                              lifecycleScope 是 Handler 的"现代封装"
                              生命周期 + 消息队列 = 异步任务的双保险

5.6 跨进程通信设计         ─→ Activity 生命周期回调通过 Binder 从 AMS 传递
                              onCreate/onResume/onDestroy 都跨进程而来
                              ServiceConnection 必须与组件生命周期对齐

5.8 页面导航与路由设计     ─→ 路由跳转 = startActivity = 触发完整生命周期序列
                              backstack 决定 Activity 何时进入 stopped
                              路由的"页面栈"就是生命周期的"时间轴"

5.9 响应式数据绑定设计     ─→ LiveData/StateFlow 必须绑 LifecycleOwner
                              "响应式" + "生命周期" 是孪生兄弟
                              数据流必须随组件死亡而停止

跨卷呼应:
- 第 3 卷·并发之道         ─→ lifecycleScope / viewModelScope 是协程生命周期管理
- 第 4 卷·内存的真相       ─→ 生命周期错乱 = 内存泄漏的 80% 根因
- 第 6 卷·包管理与构建     ─→ ViewModelProvider 用反射 + 工厂模式注入
- 第 7 卷·安全与加密       ─→ 敏感数据应在 onStop 时清空(被截屏防护)

# 10.3 通用心智口诀

1. 生命周期 = 「时间维度的有限状态机 + 钩子」 → 任何组件框架都遵循
2. 钩子是"资源管理点",不是"业务执行点" → 业务放 ViewModel,钩子做绑定/解绑
3. 配对原则:onCreate↔onDestroy、onResume↔onPause → 申请必有释放
4. 异步任务必须绑作用域 → lifecycleScope / .task / cancellables / useEffect cleanup
5. 组件销毁 ≠ 对象释放 → 内存泄漏的 90% 根因是有人还持有
6. 时间窗口陷阱:异步回调到来时组件可能已死 → 必须做存活检查
7. 现代演进:从"手管钩子"到"框架自动管" → 函数式 + 响应式是终极方向

最终升华:

生命周期是程序员与操作系统/框架之间的契约——OS 用「有限状态机 + 钩子」给程序员一个掌控时间维度的把柄;程序员用「资源管理 + 异步绑定」把这把柄变成稳定的应用。每一种生命周期机制都是某种"时间维度的封装":Android 用 13 个钩子追求完备、iOS 用 5 个钩子追求简洁、React Hooks 用 1 个钩子追求统一、SwiftUI 把生命周期完全隐藏在状态依赖里。

§0 事故的本质是把"对象生命周期"和"业务生命周期"搞混——异步回调来时对象还在、但组件已经死了。当你能"看见"每个钩子背后的状态转换、每个异步任务背后的作用域绑定时,你才真正驾驭了生命周期。

好的生命周期设计,让程序员"忘记"它存在(开发体验);好的程序员,永远"记得"组件可能随时死亡(防御性编程)。这个矛盾贯穿了从 1995 年 MFC 的 OnCreate 到 2024 年 SwiftUI 的 .task 的所有生命周期演进——它就是这个领域的灵魂。

# 延伸阅读

  • 文档:Android Activity Lifecycle (opens new window)
  • 文档:Apple UIViewController Lifecycle (opens new window)
  • 文档:React useEffect (opens new window) · Vue Lifecycle (opens new window)
  • 文档:Compose Side-effects (opens new window) · SwiftUI Task (opens new window)
  • 论文:Out of the Tar Pit (Ben Moseley & Peter Marks, 2006) —— 关于"状态管理"的本质
  • 源码:AndroidX Lifecycle、SwiftUI runtime、React Fiber、Vue Reactivity
  • 工具:LeakCanary(Android 内存泄漏)、Instruments(iOS 内存)、React DevTools Profiler
上次更新: 2026/07/15, 11:23:11
6.跨进程的通信设计
8.页面导航与路由设计

← 6.跨进程的通信设计 8.页面导航与路由设计→

最近更新
01
audit
07-27
02
C++入门教程全章思考题汇编
07-24
03
12.技术团队建设能力
07-21
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式