在讨论 Android 性能问题的时候,卡顿、响应速度、ANR 常被放在一起。它们可能有共同原因,也可能同时出现,但不是按严重程度划分的三个等级。用户说“卡”,还需要分清是动画不连贯、操作后等得太久,还是系统触发了无响应判定。
动画或列表滚动的不连贯通常称为 Jank;应用启动、亮灭屏或场景切换耗时过长属于 响应慢(slow response / high latency);ANR 则由具体的系统超时条件触发。掉帧率、启动耗时和 ANR 率各有测量口径,分析时要分开。
本文是响应速度系列的第二篇,主要是以 Android App 冷启动为例,讲解如何使用 Systrace 来分析 App 冷启动
历史案例与版本说明(2026-10-14 核查):本文保留 2021 年的 WanAndroid 启动示例。图片混用了 OPPO、Launcher3 和魅族等不同记录,未给出一套完整的设备、固件、APK 版本和原始 Trace,不能拼成同一次启动的精确时间线。下文用 AOSP Android 9/10/11 的源码解释旧路径,并标出 Android 12 的启动屏与 Android 16 的启动统计差异;旧图中的文字标注以正文修正为准。
本文目录
Systrace (Perfetto) 工具的基本使用如果还不是很熟悉,那么需要优先去补一下 Systrace 基础知识系列,本文假设你已经熟悉 Systrace(Perfetto)的使用了
Systrace 系列文章如下
- Systrace 简介
- Systrace 基础知识 - Systrace 预备知识
- Systrace 基础知识 - Why 60 fps ?
- Systrace 基础知识 - SystemServer 解读
- Systrace 基础知识 - SurfaceFlinger 解读
- Systrace 基础知识 - Input 解读
- Systrace 基础知识 - Vsync 解读
- Systrace 基础知识 - Vsync-App :基于 Choreographer 的渲染机制详解
- Systrace 基础知识 - MainThread 和 RenderThread 解读
- Systrace 基础知识 - Binder 和锁竞争解读
- Systrace 基础知识 - Triple Buffer 解读
- Systrace 基础知识 - CPU Info 解读
- Systrace 流畅性实战 1 :了解卡顿原理
- Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析
- Systrace 流畅性实战 3 :卡顿分析过程中的一些疑问
- Systrace 响应速度实战 1 :了解响应速度原理
- Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例
- Systrace 响应速度实战 3 :响应速度延伸知识
- Systrace 线程 CPU 运行状态分析技巧 - Runnable 篇
- Systrace 线程 CPU 运行状态分析技巧 - Running 篇
- Systrace 线程 CPU 运行状态分析技巧 - Sleep 和 Uninterruptible Sleep 篇
1. 准备工作
这个案例和对应的 Systrace 偏工程化一些,省略了很多细节,因为应用的启动流程涉及的知识非常广,如果每个都细化的话,会有很大的篇幅。推荐大家看这篇文章,非常详细:Android 应用启动全流程分析
所以这里以系统 Trace 为主线,讲解应用启动时各个关键模块的大概工作流程。先看这张 Trace 与手机画面的对应图,再分段深入。Systrace 与 Perfetto 是不同的采集/分析工具,Perfetto UI 也能打开旧 Systrace 格式,并不是两个可互换的命令名。

准备时先明确启动入口、应用版本、系统 build、冷/温/热状态、编译与缓存状态、账号/网络、显示模式和温控条件。冷启动要确认目标应用进程此前不存在;从最近任务划掉不保证进程已退出,进程冷启动也不等于文件缓存、安装状态和数据全部“冷”。
- 先采一份基础系统 Trace。 Android 9(API 28)起可使用开发者选项中的 System Tracing;Android 10 起该工具保存 Perfetto 格式,见官方采集说明。按设备可用项启用调度、频率、Input、Activity/Window Manager、View/图形和 Binder 事件,覆盖点击前到关键内容就绪之后。没有某条轨道,要先检查采集配置、权限和丢事件,不能当作耗时为零。
- 需要看 IRQ 时再加入相关事件。 旧 Systrace 的
irq类别是否可用取决于设备和权限。先查看本机systrace.py --help与--list-categories,再选择类别;类别列表随设备变化,不宜照搬一整串参数。新采集可按 Perfetto 系统跟踪教程配置,IRQ handler 与线程化中断的调度记录应结合看。 - IPC 调试是可选增强。
am trace-ipc启用的是 Binder 调用跟踪,不是给全部 native/Java IPC 自动恢复函数名。AOSP Android 11 的ActivityManagerService检查调用权限,并在非 debuggable 系统上过滤非 debuggable 应用;不能简化成“必须 root”或“root 后一定全有”。Android 9 的 Java Binder 跟踪可记录调用栈和部分方法/事务名称,采集额外栈信息也有开销。需要时先启动,复现结束后停止并导出:
1 | adb shell am trace-ipc start |
导出的文件是 IPC 调试信息,不是系统 Trace 的替代品。命令失败或目标进程不符合条件时,保留基础 Binder 事件,不能把缺少方法名解释为没有调用。权限依据见 Android 11 ActivityManagerService,旧 Java 跟踪实现见 Android 9 Binder。
如果可以重新编译应用,可以在关键初始化阶段手动添加 Trace 区间,或使用 TraceFix 做字节码插桩。它只覆盖选中且可插桩的方法,不包括系统 Framework 和 native 方法,也不意味着每一个函数必有记录。接入前核对插件与 AGP/Gradle/JDK 的版本矩阵;定位时缩小插桩范围,最终耗时回归要控制插桩和调试开销。
没有该插件时,仍可看到已有的 Framework、系统和应用自定义 Trace 点:

插桩后,可以看到更多应用方法区间。这两图用于比较可观察信息,不足以证明插桩对耗时没有影响:

2. Android App 冷启动流程分析
本文以 从桌面冷启动 Android App 为例,沿着触摸输入、启动请求、进程初始化到关键界面内容就绪的链路分析。下面是参与的模块,不是六段可以直接相加的串行耗时;启动动画与应用初始化往往重叠:
- 触摸屏中断处理阶段
- InputReader 和 InputDispatcher 处理 input 事件阶段
- Launcher 处理 input 事件阶段
- SystemServer 处理启动事件
- 启动动画
- 应用启动和自身逻辑阶段
先约定测量起点:物理接触、Input Down、Input Up、Launcher 发出请求,还是系统接收启动 Intent。它们不是同一时刻。终点也应分别记录首次反馈、目标 Activity 首帧和关键内容可用;TTID/TTFD 不包含相同的区间,更不自动等于物理触摸到屏幕发光的时间。本文主要定位软件链路,物理触控到显示需要另外的同步测量。
下面沿这些模块查看 Trace。
2.1 触摸屏中断处理阶段
这个历史示意图中可以看到名为 irq/382-sec_ts 的线程化中断。它是软件可观察的一个早期位置,不是手指接触屏幕的精确时刻;中断名称、编号及触控上报方式取决于设备:

对应的 CPU 调度轨道和 IRQ 轨道如下。线程运行可来自调度事件,硬中断处理区间需要相应 IRQ 事件,不能把两者当成同一条记录:

触控驱动经 Linux input/evdev 上报事件,EventHub 读取后交给 InputReader 解释,再由 InputDispatcher 派发。中断次数不与 Java MotionEvent 一一对应。图中的 2.427 ms 是选区,不能直接当作传感器延迟或全部 IRQ 的 CPU 执行时间;要归因,还需唤醒、调度与驱动事件。
2.2 InputReader 和 InputDispatcher 处理 Input 事件阶段
InputReader 和 InputDispatcher 这两个线程跑在 SystemServer 里面,专门负责处理 Input 事件,具体的流程可以参考Android Systrace 基础知识 - Input 解读 这篇文章

示例按一次点击标出了 Down、若干 Move 和 Up。点击可以没有 Move,也可能被 Cancel、移动距离或长按逻辑取消;这些动作类型应由事件详情确认,不能只看几个相似的函数区间。
Launcher 的窗口建立输入通道后,InputDispatcher 根据触摸位置、窗口触摸区域、层级和输入策略选择目标;不是进程创建或“前台可见”就保证收到所有输入。input 事件在 SystemServer 和 App 中的流转可以参考 Android Systrace 基础知识 - Input 解读 ,这里把核心的两张图放上来
2.2.1 Input 事件在 SystemServer 中流转
看下图即可,如果要看更详细的,可以查看 Android Systrace 基础知识 - Input 解读
2.2.2 Input 事件在 Launcher 进程流转
看下图即可,如果要看更详细的,可以查看 Android Systrace 基础知识 - Input 解读
这两张旧图来自魅族 Launcher,用于解释队列,不是上面 WanAndroid 示例的同一份记录。wq 减少通常表示 Dispatcher 收到处理完成的确认,不表示画面已经呈现。第二张图的 aq 标注也要修正:在 Android 10 ViewRootImpl 中,pending 计数是在取出事件、调用 deliverInputEvent 之前 减少,归零不能证明处理已完成。
2.3 Launcher 进程处理 Input 事件阶段
Launcher 的处理可以分开观察两个区间:
- Down 到按压反馈帧:例如图标变灰或缩小。状态更新和该帧实际呈现不是同一时刻。
- Up/点击判定到发出启动请求:这段更适合检查 Launcher 自身延迟。若从 Down 或首个反馈帧开始计时,还会包含用户按住图标的时间,不能全部算 Launcher 处理慢。
另外提一下,滑动桌面到桌面第一帧响应时间(这个指的是滑动桌面的场景,左右滑动桌面的时候,用高速相机拍摄,从手指动开始,到桌面动的第一帧的时间)也是一个很重要的响应速度指标,部分厂商也会在这方面做优化,感兴趣的可以自己试试主流厂商的桌面滑动场景。比较不同设备时,还需对齐刷新率、手势速度、内容和测量起终点;仅对比 Trace 不能替代物理触摸到画面变化的测量
通常的图标点击在 Up 后通过点击判定,再发出启动 Activity 的请求。Android 10 起主要由 SystemServer 中的 ActivityTaskManagerService(ATMS)处理 Activity/Task,AMS 等模块参与进程管理;下图里的代理名也是 IActivityTaskManager。通知或深链启动不一定经过 Launcher。

这一阶段有助于区分应用启动前的系统/Launcher 延迟和应用自己的初始化工作。动画由哪些进程协调取决于系统版本与桌面实现。图标动画、窗口过渡和应用准备可能并行;慢镜头适合观察连续性,但不能单独确定代码瓶颈。
2.4 SystemServer 处理 StartActivity 阶段
这里关注三类工作:处理启动请求与任务/窗口状态、协调已有 Activity 的生命周期、在需要时请求创建目标进程。它们可能跨线程交错,不应当作固定顺序的三个连续区间;多窗口或不同启动模式也会改变路径。
处理启动命令
这个 SystemServer 进程中的 Binder 调用就是 Launcher 通过 ActivityTaskManager.getService().startActivity 调用过来的

目标进程不存在时,系统需要创建进程后才能在其中启动 Activity。常见路径是 SystemServer 通过 socket 请求与应用 ABI 匹配的 Zygote 创建子进程,不是 SystemServer 自己执行该次 fork。设备不一定同时提供 32 位和 64 位 Zygote;Android 10 的 ZygoteProcess 已包含可选 USAP 进程池路径,也不能要求每次启动都出现同样的即时 fork 区间。


图里的 appZygote.getProcess().start(...) 是 App Zygote 相关分支,不能拿它代表普通 Activity 冷启动的全部实现。Start proc 的 7.343 ms 选区也不等于纯 fork CPU 耗时,它可能包含构造请求、通信和等待结果。
Zygote 64 位进程执行 Fork 操作


上面的 Java 方法是调用方写入 Zygote socket 并读取 PID/状态的通信代码,不是 Zygote 侧执行 fork 的函数。可对照 Android 10 ZygoteProcess 的普通请求和 USAP 分支,再结合对应进程、线程和调度事件识别实际路径。
对应的 App 进程出现

下面是 ActivityThread.main 的旧源码截图:准备主 Looper、创建 ActivityThread、attach 到系统,再进入 Looper.loop。截图指向当时的 master,没有固定提交;它用于说明进程入口,不能据此确定所有截图的 Android 版本。

图中 launching: com.aboback.wanandroidjetpack 的选区为 904.966 ms,是系统启动跟踪区间,不包含用户此前按压图标的全部时间,也不能直接当作内容完全就绪。需要检查该版本记录的起止点和目标 Activity。对于 SplashActivity → MainActivity,不能一律断言统计只到 SplashActivity;例如 Android 16 ActivityMetricsLogger 包含连续启动合并逻辑。应查看实际关联的 Activity 和窗口绘制事件。

2.5 应用进程启动阶段
本案例有独立 SplashActivity,可按下面三个里程碑分段。它不是所有应用都必须经历的结构;“界面稳定”也不够精确,应写明首屏哪些内容可见、哪些操作可用,以及错误/空数据状态如何判定。
- 应用进程启动到 SplashActivity 第一帧显示(部分 App 没有 SplashActivity,所以可以省略这一步,直接到进程启动到 主 Activity 第一帧显示 )
- SplashActivity 第一帧显示到主 Activity 第一帧显示
- 主 Activity 第一帧显示到界面完全显示
下面按案例中的三个阶段分析;直接进入主 Activity 的应用可跳过中间的 Activity 切换。
应用进程启动到 SplashActivity 第一帧显示
目标进程启动后,要经历运行时入口、ActivityThread.main 与 attach,系统再调度 bindApplication 等工作,不是 fork 完就立即执行应用的 onCreate。bindApplication 是进程初始化,不是每次 Activity 启动都执行;服务或广播等此前已拉起该进程时,后续打开 Activity 可能复用它。
在 AOSP android-11.0.0_r1 ActivityThread 的正常绑定路径中,先创建 Application 并完成 attach,再安装本进程需初始化的 ContentProvider,之后调用 Application.onCreate。因此,Provider 初始化可能比 Application.onCreate 更早,不能把它们都归到后续组件阶段。
常见 Activity 前台创建顺序是 onCreate → onStart → onResume。旧 View/HWUI 示例随后可观察 traversal 的 measure/layout/draw、RenderThread 工作及图形管线,但这不是“执行一次 doFrame 再加一个固定 Vsync 周期”就一定显示。GPU、Buffer、SurfaceFlinger、HWC 与显示设备还要完成各自阶段。
图中的 finishDrawing 是应用向 WindowManager 报告绘制进度的调用,不是物理屏幕已显示的时间戳。 Android 10 的 ViewRootImpl 通过 WindowSession 发出该通知;Android 11 WindowStateAnimator 收到后还可能处于 COMMIT_DRAW_PENDING,随后才提交/显示窗口。图内 onStart、onCreate 的顺序也应按上面的生命周期修正。更完整的链路见 MainThread 和 RenderThread 解读。

SplashActivity 第一帧显示到主 Activity 第一帧显示
这个样例从 SplashActivity 转到主 Activity,可继续跟踪生命周期、自有初始化、WebView 等工作,直到目标窗口的首帧里程碑。独立 SplashActivity 不一定用于广告,也不是“大部分应用必须有”的步骤。Android 12(API 31)起要区分系统 SplashScreen 与应用 Activity,保留独立启动页可能出现双重 splash,见官方迁移指南。
下图的 829.966 ms 是两个图示绘制通知附近的选区,不是两次物理呈现之间经过独立验证的时长。WebView 和其他工作是否处于关键路径,要看依赖与等待关系,不能按颜色或区间长度直接认定根因。

主 Activity 第一帧显示到界面完全加载并显示
主 Activity 的首帧可能只有框架,图片或列表数据稍后才加载。先定义“关键内容就绪”,再用业务标记定位其发生时刻,并关联后续绘制/呈现。onDraw、列表 itemCount 大于 0 或网络请求完成,任何一个单独发生都不保证用户已看到完整界面或能够操作。

图中 1.648 s 是当时选定的后续加载区间。没有同步的原录像、业务就绪标记和完整 Trace,不能仅凭最后一个 doFrame 确认全部内容就绪,也不能与其他设备的示意图耗时相加。
新项目可以同时保留两类指标:系统的 TTID(初始显示)和由应用报告的 TTFD(完整显示)。Activity.reportFullyDrawn() 从 API 19 提供,要在约定的关键内容/可用条件满足后报告,而不是为了缩短数字提前调用。可用 Macrobenchmark StartupTimingMetric 重复测量 timeToInitialDisplayMs 与 timeToFullDisplayMs;后者结合 fully-drawn 信号及相关帧完成,不是简单给任意业务 Trace 区间改名。缺少报告或采集不全的样本单列,不记作 0。
复测时固定启动入口与冷/温/热模式、编译策略和数据条件,保留每次 Trace,再比较中位数和慢样本。首帧变快而内容更晚可用,不算完整响应一起改善。
我制作了一个 Systrace + 截图的方式,来进行演示,方便你了解 App 启动各个阶段都对应在 Systrace 的哪里(使用的是一个开源的 WanAndroid 客户端)

End
本文重点放在了如何在 Systrace 中展示 App 的完整冷启动流程,方便大家在做 App 的启动优化的时候,可以通过 Systrace 来快速定位到启动瓶颈,也方便进行竞品的对比和分析,
- 至于如何分析,可以查看 Systrace 响应速度实战 1 :了解响应速度原理 的分析套路部分
- 至于如何优化,可以查看 Android App 启动优化全记录 这篇文章,这里就不再重复了。不过随着技术的发展,有些优化手段会消失,而会有新的优化手段冒出来,我也会对这篇文章进行维护,如果大家发现新的优化技术,麻烦博客留言或者加微信(553000664)通知我,我来进行调研和更新
系列文章
- Systrace 响应速度实战 1 :了解响应速度原理
- Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例
- Systrace 响应速度实战 3 :响应速度延伸知识
- Systrace 基础知识系列-放个链接在这里方便大家直接点过去
参考文章
- Android 应用启动全流程分析
- 探究 | App Startup 真的能减少启动耗时吗
- Jetpack 新成员,App Startup 一篇就懂
- App Startup
- Android App 启动优化全记录
- Android application profiling
关于我 && 博客
下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!
- 博主个人介绍 :里面有个人的微信和微信群链接。
- 本博客内容导航 :个人博客内容的一个导航。
- 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
- Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。
一个人可以走的更快 , 一群人可以走的更远
