第1章:启动阶段拆解——应用进程创建、Application初始化、Activity生命周期与关键时间节点
1.1 启动流程总览
应用启动并非单一瞬间动作,而是操作系统与Android框架协作完成的串行过程。整个过程可拆解为四个子阶段:
- 进程创建:Zygote进程收到AMS请求,fork出新进程并运行ActivityThread。
- Application初始化:创建Application对象,调用attachBaseContext()和onCreate()。
- Activity生命周期启动:创建目标Activity,走完onCreate/onStart/onResume。
- 首帧绘制:View树完成测量、布局、绘制,并送出第一帧。
开发者可直接干预的监控时间节点有三个:processStart、applicationOnCreate、activityOnResume。其余系统内核阶段只能通过trace和日志间接观测。
1.2 关键阶段与时间节点对照
| 子阶段 | 关键动作 | 时间节点/API | 监控数据来源 |
|---|---|---|---|
| 进程创建 | Zygote fork → ActivityThread.main() | processStart | Logcat (Zygote) |
| Application初始化 | attachBaseContext → onCreate | applicationOnCreate | 手动埋点 + API |
| Activity生命周期 | onCreate → onStart → onResume | activityOnResume | ReportFullyDrawn |
| 首帧绘制 | ViewRootImpl.performTraversals | frameCallback | Choreographer |
1.3 启动流程SVG框架图
下图展示了从用户点击图标到首帧显示的完整启动阶段,包含核心节点与系统边界:
1.4 进程创建阶段详解
用户点击桌面图标后,Launcher通过Binder向ActivityManagerService(AMS)发送启动Intent。AMS首先判断目标进程是否已存在:
- 若存在且已绑定,直接复用进程并走Application生命周期。
- 若不存在,AMS通过Socket向Zygote进程发送startProcess请求。
Zygote收到请求后执行ZygoteConnection.processOneCommand(),完成以下工作:
- fork()创建子进程,进程ID为新值。
- 子进程继承Zygote的预加载资源(ClassLoader、系统Framework类、共享库等)。
- 子进程调用
ActivityThread.main()进入应用主循环。 - 主线程创建
ActivityThread实例并绑定ApplicationThread(用于与AMS通信的Binder)。
监控指标:从AMS发送请求到ActivityThread.main()开始执行的时间差。此值通常在200~800ms(取决于设备性能和Zygote堆大小)。无法用Android SDK API直接获取,需通过adb logcat -b all | grep “Zygote”或systrace观测。
1.5 Application初始化阶段详解
当ActivityThread准备就绪后,AMS通过Binder向应用进程发送bindApplication消息。此时进程内部执行顺序固化:
- 创建
ContextImpl并调用Application.attachBaseContext()。 - 执行
Application.onCreate()——这是开发者常用的初始化入口。 - 创建并初始化所有注册的
ContentProvider,依次调用其onCreate()。
在onCreate()中,开发者常做以下耗时代码操作,需重点监控:
// 示例:监控 Application.onCreate 耗时
class MyApp : Application() {
override fun onCreate() {
val start = System.currentTimeMillis()
super.onCreate()
// … SDK 初始化等代码 …
val elapsed = System.currentTimeMillis() - start
Log.d("AppStartup", "Application.onCreate 耗时: $elapsed ms")
}
}
1.6 Activity生命周期阶段详解
Application初始化完成后,AMS向ActivityThread发送launchActivity事务。目标Activity依次执行:
| 回调方法 | 主要工作 | 常见耗时场景 | 监控手段 |
|---|---|---|---|
| onCreate() | setContentView、inflate布局、初始化View / Fragment / Adapter | 复杂XML解析、大量View创建、数据预加载 | 手动埋点 |
| onStart() | 准备界面可见前的轻量工作(状态恢复、注册观察者等) | 耗时短,通常<5ms | 手动埋点 |
| onResume() | 确保前台运行、开始动画/视频/传感器 | 启动动画、VP播放、网络请求 | ReportFullyDrawn |
首帧绘制发生在onResume()之后。系统通过Choreographer注册回调,在下一帧VSYNC信号到达时执行performTraversals()完成ViewRootImpl的测量、布局、绘制。首帧时间可通过Activity.reportFullyDrawn()(API 24+)或Choreographer的帧回调精确捕获。
1.7 关键时间节点定义与取值
| 时间节点 | 定义 | 建议取值API |
|---|---|---|
| processStart | Zygote完成fork并开始执行ActivityThread.main() | systrace/logcat |
| applicationOnCreate | Application.onCreate() 执行结束 | System.currentTimeMillis() |
| activityOnResume | 目标Activity.onResume() 执行结束 | 手动埋点 + reportFullyDrawn |
| firstFrameDrawn | ViewRootImpl完成首帧绘制并提交到SurfaceFlinger | Choreographer.postFrameCallback |
ContentResolver.query() 或 SharedPreferences.getString() 等I/O操作,它们会触发磁盘读或ContentProvider创建,直接增加启动耗时 50~300ms。例如:// 错误示例 → 阻塞启动
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
val name = getSharedPreferences("pref", 0).getString("key", "") // 磁盘I/O!
val cursor = contentResolver.query(Uri.parse("…"), null, null, null) // 跨进程!
}
}
1.8 本章小结
应用启动耗时可划分为4个子阶段,其中开发者能直接干预和监控的是Application初始化与Activity生命周期。定义清晰的processStart、applicationOnCreate、activityOnResume、firstFrameDrawn四个时间节点,是后续启动优化的数据基础。本章的拆解方法将贯穿整个课程,每阶段耗时优化都会回溯到这里的定义和边界。