Activity 生命周期完整解析
前言
Activity 生命周期最容易被讲成一张“背诵图”:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()。但线上问题往往不发生在这张图本身,而发生在几个边界上:
- 页面不可见了,定位、相机、网络流还在跑。
- 旋转屏幕后页面重建,表单、滚动位置或临时筛选条件丢失。
onPause()里做了重活,导致下一个页面启动变慢。- 进程被系统回收后重新进入,
ViewModel没了,但任务栈和保存状态还在。 - Compose 页面用
collectAsState()持续收集 Flow,后台仍然消耗资源。
理解 Activity 生命周期,不是为了在每个回调里都写代码,而是为了判断一段资源、状态或异步任务应该绑定到哪一种“生命长度”:实例生命周期、可见生命周期、前台交互生命周期、任务栈保存状态,还是进程生命周期。
本文基于 Android Developers 官方文档、Android 16 / API 36 平台文档和 AOSP master 源码,系统整理 Activity 生命周期的 API 行为、源码分发链路、状态保存、实战用法、性能优化和面试考点。
前置知识
建议先理解:
- A019:Android 应用进程、组件与系统架构概览。
Intent、任务栈、窗口可见性、主线程消息循环的基本概念。- Jetpack
ViewModel、SavedStateHandle、Lifecycle、Kotlin Flow 的基础用法。
本文的下一篇是 A021 Activity 启动模式与任务栈。A020 关注“一个 Activity 实例如何被创建、暂停、停止、恢复和销毁”,A021 再讨论“多个 Activity 如何被组织成任务栈”。
核心概念
六个核心回调
Android 官方文档将 Activity 的核心生命周期回调归纳为六个:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()。此外,onRestart() 用于从 Stopped 返回 Started 前的过渡,onSaveInstanceState() 和 onRestoreInstanceState() 用于保存和恢复临时 UI 状态,但它们不是“每次都必然成对出现”的业务生命周期入口。
| 回调 | 状态含义 | 适合做什么 | 不适合做什么 |
|---|---|---|---|
onCreate() | Activity 实例首次创建或重建 | 初始化依赖、设置 UI、读取 savedInstanceState | 直接启动长时间阻塞任务 |
onStart() | 页面变为可见 | 注册和可见性相关的监听、开始轻量刷新 | 申请只在前台交互时才需要的独占资源 |
onResume() | 页面进入前台并可交互 | 启动相机预览、传感器、动画、短周期刷新 | 做数据库迁移、网络同步等重操作 |
onPause() | 页面失去前台交互 | 暂停动画、提交轻量草稿、释放前台独占资源 | 阻塞保存大量数据 |
onStop() | 页面不可见 | 解除可见性监听、停止 UI 刷新、释放可见资源 | 依赖它保存唯一关键业务数据 |
onDestroy() | 当前实例销毁 | 最后兜底清理、释放未释放资源 | 判断所有业务结束原因 |
三个生命周期范围
Android API reference 对 Activity 提供了三个很实用的观察范围:
| 范围 | 起点 | 终点 | 典型资源 |
|---|---|---|---|
| 整个实例生命周期 | onCreate() | onDestroy() | 只属于当前 Activity 实例的对象 |
| 可见生命周期 | onStart() | onStop() | UI 监听、定位粗粒度更新、页面可见埋点 |
| 前台生命周期 | onResume() | onPause() | 相机、麦克风、传感器、动画、输入焦点 |
开发时应先问“资源需要活多久”,再决定放在哪个回调,而不是看到生命周期回调就塞逻辑。
Activity 被销毁不等于进程被销毁
Activity 是运行在应用进程里的对象。系统为了释放内存时,并不会单独“杀掉一个 Activity 对象”来回收内存,而是终止承载它的应用进程。用户旋转屏幕、切换深色模式、进入多窗口等配置变化,则通常会销毁当前 Activity 实例并创建新实例。两者表现都可能是重新执行 onCreate(),但状态恢复策略不同:
- 配置变化:
ViewModel通常可以保留,Activity 实例会变。 - 系统回收进程后恢复:
ViewModel不再存在,需要依赖SavedStateHandle、onSaveInstanceState()或持久化数据。 - 用户主动结束任务、从最近任务划掉、强行停止应用:很多临时保存状态不会被恢复。
整体架构
Activity 生命周期不是 Activity 自己“自发变化”的结果,而是系统服务、应用主线程和 Framework 回调协作的结果。
flowchart TD
User["用户/系统事件"]
ATMS["system_server: ATMS/AMS"]
Binder["Binder 调度"]
AppThread["app process: ApplicationThread"]
MainThread["ActivityThread + H Handler"]
Transaction["ClientTransaction / TransactionExecutor"]
Instrumentation["Instrumentation"]
Activity["Activity callbacks"]
Lifecycle["AndroidX Lifecycle observers"]
User --> ATMS
ATMS --> Binder
Binder --> AppThread
AppThread --> MainThread
MainThread --> Transaction
Transaction --> Instrumentation
Instrumentation --> Activity
Activity --> Lifecycle
这张图的关键点是线程边界:生命周期回调最终在应用进程的主线程执行。你在 onCreate()、onResume()、onPause() 里阻塞主线程,不只是当前页面卡顿,还可能阻塞下一个生命周期事务的执行。
工作流程
首次启动
典型首次启动顺序是:
onCreate()
onStart()
onResume()
onCreate() 用来建立页面结构,onStart() 表示页面已经可见,onResume() 表示页面可以交互。很多页面看起来只经历了一瞬间,但 Framework 内部仍然按事务把生命周期推进到目标状态。
打开新页面或退到后台
当前 Activity 被新页面完全遮挡,常见顺序是:
onPause()
onStop()
如果只是弹出透明 Activity、权限弹窗、来电浮层或进入多窗口,当前 Activity 可能只进入 Paused,仍然部分可见。不要把“不可交互”和“不可见”混为一谈。
从后台返回
如果 Activity 实例还在内存中:
onRestart()
onStart()
onResume()
如果进程被系统回收,返回时会创建新实例:
onCreate(savedInstanceState != null)
onStart()
onResume()
这也是为什么只用 ViewModel 不能覆盖所有状态恢复场景。ViewModel 适合配置变化和内存中的页面数据,SavedStateHandle 或 onSaveInstanceState() 适合保存可序列化的小体积 UI 状态,数据库或 DataStore 适合持久业务数据。
结束页面
用户返回或代码调用 finish() 时,常见顺序是:
onPause()
onStop()
onDestroy()
但不要把 onDestroy() 当成可靠的“最终保存”入口。极端情况下,进程可能被直接终止;更稳妥的做法是把关键业务数据在用户操作发生时及时提交,或在 onPause() 做轻量保存。
状态图
stateDiagram-v2
[*] --> Created: onCreate
Created --> Started: onStart
Started --> Resumed: onResume
Resumed --> Paused: onPause
Paused --> Resumed: onResume
Paused --> Stopped: onStop
Stopped --> Started: onRestart + onStart
Stopped --> Destroyed: onDestroy
Paused --> Destroyed: finish / config change
Destroyed --> [*]
这个图只表达主干路径。真实设备上还要考虑透明页面、多窗口、画中画、权限弹窗、配置变化、进程回收和 Activity Result 等场景。
API 使用
1. 把资源绑定到正确的生命周期
下面例子用 DefaultLifecycleObserver 把相机预览绑定到前台生命周期。Activity 只负责接入,资源管理下沉到独立组件。
class CameraPreviewController(
private val camera: CameraSession,
) : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
camera.startPreview()
}
override fun onPause(owner: LifecycleOwner) {
camera.stopPreview()
}
}
class ScanActivity : ComponentActivity() {
private val cameraSession by lazy { CameraSession(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(CameraPreviewController(cameraSession))
setContent {
ScanScreen()
}
}
}
这种写法的好处是:Activity 不会变成资源回调的堆砌容器,测试时也更容易单独验证 CameraPreviewController。
2. Compose 中用生命周期感知收集 Flow
Android Developers 文档建议 Compose 页面使用生命周期感知的状态收集方式,避免页面后台后仍持续收集上游 Flow。
@Composable
fun OrderRoute(
viewModel: OrderViewModel,
modifier: Modifier = Modifier,
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
OrderScreen(
uiState = uiState,
onRetry = viewModel::retry,
modifier = modifier,
)
}
ViewModel 侧也应让上游流在没有订阅者时停下来:
@HiltViewModel
class OrderViewModel @Inject constructor(
repository: OrderRepository,
) : ViewModel() {
val uiState: StateFlow<OrderUiState> =
repository.observeOrders()
.map { orders -> OrderUiState.Content(orders) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = OrderUiState.Loading,
)
}
Activity 生命周期负责“页面是否可见”,ViewModel 生命周期负责“页面数据是否仍被持有”。两者要配合,而不是互相替代。
3. 保存小体积 UI 状态
onSaveInstanceState() 适合保存能快速序列化的小状态,例如当前 tab、输入草稿 ID、滚动位置 key。不要塞 Bitmap、大列表或复杂对象。
class SearchActivity : ComponentActivity() {
private var selectedTab: String = "all"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
selectedTab = savedInstanceState?.getString(KEY_SELECTED_TAB) ?: "all"
setContent {
SearchScreen(selectedTab = selectedTab)
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putString(KEY_SELECTED_TAB, selectedTab)
super.onSaveInstanceState(outState)
}
private companion object {
const val KEY_SELECTED_TAB = "selected_tab"
}
}
如果状态本来属于业务数据,例如订单详情、编辑中的长文本、已上传文件列表,应尽早写入数据库或草稿表,再在保存状态中记录业务 ID。
源码分析
版本与源码路径
本文源码分析基于 AOSP master,时间点为 2026-06-25。具体厂商 ROM 可能有定制,精确行号和细节以目标版本源码为准。
| 类/目录 | 路径 | 作用 |
|---|---|---|
Activity | frameworks/base/core/java/android/app/Activity.java | 定义生命周期回调和窗口、Intent、状态保存等 API |
ActivityThread | frameworks/base/core/java/android/app/ActivityThread.java | 应用进程主线程管理者,执行生命周期事务 |
Instrumentation | frameworks/base/core/java/android/app/Instrumentation.java | 创建 Activity,包装并调用生命周期方法 |
servertransaction | frameworks/base/core/java/android/app/servertransaction/ | 用事务对象表达 launch、pause、resume、stop、destroy 等请求 |
分发链路
现代 Android 中,system_server 会通过 ClientTransaction 把生命周期请求发到应用进程。应用进程的 ActivityThread 收到事务后,由 TransactionExecutor 执行对应生命周期项。
sequenceDiagram
participant ATMS as system_server
participant Binder as Binder
participant Thread as ActivityThread
participant Exec as TransactionExecutor
participant Inst as Instrumentation
participant Act as Activity
ATMS->>Binder: scheduleTransaction
Binder->>Thread: ApplicationThread
Thread->>Exec: execute(transaction)
Exec->>Thread: handleLaunch/Resume/Pause/Stop
Thread->>Inst: callActivityOnXxx
Inst->>Act: onCreate/onResume/onPause...
从源码可以看到几个关键事实:
ActivityThread.performLaunchActivity()会创建ContextImpl、加载 Activity 类,并通过Instrumentation.newActivity()实例化页面。- 生命周期调用不是直接裸调 Activity 方法,而是经过
Instrumentation.callActivityOnCreate()、callActivityOnPause()、callActivityOnDestroy()等包装入口。 ActivityThread会检查开发者是否调用了super。如果没有调用,可能抛出SuperNotCalledException。performPauseActivity()、performStopActivityInner()、performDestroyActivity()都运行在应用主线程,慢操作会阻塞后续事务。
onStop() 与 onSaveInstanceState() 的顺序变化
一个容易被忽略的源码细节是:AOSP ActivityThread.callActivityOnStop() 附近的注释说明,Android P 之前 onSaveInstanceState() 在 onStop() 之前调用;从 Android P 开始,通常在 onStop() 之后调用;Honeycomb 之前还有更早的保存时机。
这意味着两个实践结论:
- 不要写依赖固定相对顺序的业务逻辑,例如“我一定能在
onStop()前从 Bundle 读到某个值”。 onSaveInstanceState()只做状态快照,不做业务提交;业务提交应由用户动作、ViewModel 或 Repository 负责。
线程与进程模型
| 层级 | 所在位置 | 说明 |
|---|---|---|
| ATMS/AMS | system_server 进程 | 管理任务、Activity 记录、可见性和目标生命周期 |
| Binder | 跨进程通信 | 把事务发到应用进程 |
| ActivityThread | 应用进程主线程 | 执行生命周期、创建对象、分发回调 |
| Activity 回调 | 应用进程主线程 | 开发者代码实际运行的位置 |
| ViewModel/Lifecycle | 应用进程 | 与 Activity/Fragment 生命周期关联,不等于系统组件本身 |
生命周期回调“看起来是系统调用”,但你的代码仍在应用主线程里执行。任何磁盘 IO、网络请求、复杂 JSON 解析、大图解码、数据库批量写入,都应离开生命周期主路径。
实战案例
场景:扫码页面的相机、网络和草稿状态
假设有一个扫码下单页面:
- 页面可交互时打开相机预览。
- 页面可见时监听订单状态。
- 用户输入的备注需要在旋转屏幕后保留。
- 提交订单必须可靠保存。
推荐拆分如下:
| 内容 | 生命周期归属 | 推荐实现 |
|---|---|---|
| 相机预览 | onResume() 到 onPause() | DefaultLifecycleObserver 或 LifecycleResumeEffect |
| 订单状态 Flow | STARTED 到 STOPPED | collectAsStateWithLifecycle() |
| 备注输入框 | 配置变化和进程恢复 | SavedStateHandle 或 rememberSaveable |
| 已提交订单 | 持久业务数据 | Repository 写数据库/网络,不依赖 onStop() |
| 页面埋点 | 可见生命周期 | onStart() 开始,onStop() 结束 |
一个常见错误是把所有清理都放在 onDestroy():扫码页被新页面遮挡时,相机可能仍占用;系统回收进程时,onDestroy() 也不一定给你完成复杂清理的机会。更好的设计是按资源属性选择生命周期范围。
调试生命周期问题
可以临时加一个统一日志工具,不要在每个回调里散落字符串:
class LifecycleLogger(
private val name: String,
private val log: (String) -> Unit = { message -> Log.d("LifecycleLogger", message) },
) : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) = log("$name -> ON_CREATE")
override fun onStart(owner: LifecycleOwner) = log("$name -> ON_START")
override fun onResume(owner: LifecycleOwner) = log("$name -> ON_RESUME")
override fun onPause(owner: LifecycleOwner) = log("$name -> ON_PAUSE")
override fun onStop(owner: LifecycleOwner) = log("$name -> ON_STOP")
override fun onDestroy(owner: LifecycleOwner) = log("$name -> ON_DESTROY")
}
在 Activity 中接入:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(LifecycleLogger(javaClass.simpleName))
}
调试时重点观察四类场景:启动新页面、按 Home、旋转屏幕、从最近任务杀掉后重进。只测“打开和返回”不足以覆盖真实生命周期。
性能优化
1. onPause() 必须快
官方 API 文档强调,onPause() 应快速完成,因为下一个 Activity 的恢复可能要等当前 Activity 暂停完成。实践中应避免:
- 同步写大文件。
- 批量数据库事务。
- 上传埋点并等待结果。
- 停止复杂服务时阻塞主线程。
可替代方案:
- 用户输入时增量写草稿。
onPause()只触发轻量状态标记。- 重任务交给
viewModelScope、Repository、WorkManager 或后台线程。
2. 按资源可见性释放
不要等 onDestroy() 才释放高成本资源:
- 相机、麦克风:通常在
onResume()获取,在onPause()释放。 - GPS 或传感器:按前台或可见范围注册,后台降级或停止。
- 动画和刷新 ticker:页面不可见时停止。
- BroadcastReceiver:只影响 UI 的动态接收器可放在
onStart()/onStop()。
3. 避免后台无效收集
Compose 页面中,Android app 优先使用 collectAsStateWithLifecycle()。如果 ViewModel 上游使用 stateIn(SharingStarted.Eagerly),即使 UI 不可见也可能持续拉取数据;对网络、定位、数据库观察者这类资源,通常应考虑 SharingStarted.WhileSubscribed(...)。
4. 控制保存状态体积
Bundle 经过 Binder/Parcel 相关路径时有体积和序列化成本。保存状态应只放:
- 当前 tab、筛选条件、滚动 key。
- 表单中短文本或草稿 ID。
- 业务实体 ID,而不是实体全量内容。
大对象应放数据库、文件或内存缓存中,保存状态只记录索引。
常见问题
onCreate() 会不会只调用一次?
不会。它只对“同一个 Activity 实例”调用一次。配置变化、系统回收进程后恢复、重新启动页面,都会创建新实例并再次调用 onCreate()。
onDestroy() 一定会调用吗?
不应依赖它完成关键保存。正常 finish 或配置变化通常会调用,但系统终止进程时,你不应该假设复杂业务逻辑还能在 onDestroy() 中完成。
onSaveInstanceState() 是不是生命周期回调?
它是 Activity 状态保存回调,不是六个核心生命周期之一。它的调用时机和是否调用取决于场景,不适合承载业务提交。
ViewModel 能不能替代 onSaveInstanceState()?
不能完全替代。ViewModel 能跨配置变化保留内存数据,但进程死亡后 ViewModel 会消失。需要恢复用户可感知的临时 UI 状态时,应结合 SavedStateHandle、rememberSaveable、onSaveInstanceState() 或持久化存储。
Compose 单 Activity 架构还需要懂 Activity 生命周期吗?
需要。即使页面导航都在 Compose 内完成,相机、权限、窗口、系统回收、进程恢复、Flow 收集、后台资源释放仍然受 Activity 和 LifecycleOwner 影响。
onStart() 和 onResume() 怎么选?
看资源是否要求“用户可交互”。只要页面可见就需要的监听放 onStart()/onStop();必须在前台、拿到焦点或独占硬件的资源放 onResume()/onPause()。
面试考点
- Activity 从首次启动到可交互的生命周期顺序是什么?
onStart()与onResume()的区别是什么?分别适合管理什么资源?- 打开一个完全遮挡的新 Activity,旧 Activity 通常会经历哪些回调?
- 透明 Activity 或权限弹窗出现时,旧 Activity 是否一定会
onStop()? - 配置变化和系统回收进程后的恢复有什么区别?
- 为什么不建议在
onPause()中做耗时操作? onSaveInstanceState()适合保存什么?不适合保存什么?- ViewModel、SavedStateHandle、数据库三者在状态恢复中的边界是什么?
- Activity 生命周期回调最终运行在哪个线程?源码中大致经过哪些类?
- Android P 前后
onSaveInstanceState()与onStop()的顺序有什么源码层面的变化?
高级追问可以继续展开:
ActivityThread为什么要通过Instrumentation调用生命周期?- 不调用
super.onPause()可能发生什么? - 单 Activity + Compose 架构下,页面级生命周期应如何建模?
- 多窗口、画中画、折叠屏对“可见”和“前台”的理解有什么影响?
总结
Activity 生命周期可以浓缩成一个工程模型:
onCreate()到onDestroy()管 Activity 实例。onStart()到onStop()管页面可见。onResume()到onPause()管前台交互和独占资源。onSaveInstanceState()、SavedStateHandle和持久化存储共同解决状态恢复。- Framework 通过
ClientTransaction、ActivityThread、Instrumentation在应用主线程分发生命周期。 - 性能优化的核心是让生命周期回调足够轻,把资源绑定到正确范围,把业务状态及时写入正确层。
真正成熟的 Activity 生命周期代码,往往不是重写很多回调,而是让 Activity 变薄:页面状态交给 ViewModel,业务数据交给 Repository,资源控制交给生命周期感知组件,UI 收集交给 collectAsStateWithLifecycle()。
扩展阅读
- Android Developers: The activity lifecycle
- Android Developers API Reference: Activity
- Android Developers: Lifecycle in Jetpack Compose and lifecycle-aware components
- Android Developers: Save UI states
- Android Developers: Use Kotlin coroutines with lifecycle-aware components
- Android Developers: SDK Platform release notes, Android 16 / API level 36
- AOSP:
ActivityThread.java - AOSP:
Activity.java - AOSP:
Instrumentation.java - AOSP:
android.app.servertransaction - 系列下一篇:A021 Activity 启动模式与任务栈