EN

Android Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例

字数统计: 5.1k阅读时长: 18 min
2021/09/13
loading

在讨论 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 系列文章如下

  1. Systrace 简介
  2. Systrace 基础知识 - Systrace 预备知识
  3. Systrace 基础知识 - Why 60 fps ?
  4. Systrace 基础知识 - SystemServer 解读
  5. Systrace 基础知识 - SurfaceFlinger 解读
  6. Systrace 基础知识 - Input 解读
  7. Systrace 基础知识 - Vsync 解读
  8. Systrace 基础知识 - Vsync-App :基于 Choreographer 的渲染机制详解
  9. Systrace 基础知识 - MainThread 和 RenderThread 解读
  10. Systrace 基础知识 - Binder 和锁竞争解读
  11. Systrace 基础知识 - Triple Buffer 解读
  12. Systrace 基础知识 - CPU Info 解读
  13. Systrace 流畅性实战 1 :了解卡顿原理
  14. Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析
  15. Systrace 流畅性实战 3 :卡顿分析过程中的一些疑问
  16. Systrace 响应速度实战 1 :了解响应速度原理
  17. Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例
  18. Systrace 响应速度实战 3 :响应速度延伸知识
  19. Systrace 线程 CPU 运行状态分析技巧 - Runnable 篇
  20. Systrace 线程 CPU 运行状态分析技巧 - Running 篇
  21. Systrace 线程 CPU 运行状态分析技巧 - Sleep 和 Uninterruptible Sleep 篇

1. 准备工作

这个案例和对应的 Systrace 偏工程化一些,省略了很多细节,因为应用的启动流程涉及的知识非常广,如果每个都细化的话,会有很大的篇幅。推荐大家看这篇文章,非常详细:Android 应用启动全流程分析

所以这里以系统 Trace 为主线,讲解应用启动时各个关键模块的大概工作流程。先看这张 Trace 与手机画面的对应图,再分段深入。Systrace 与 Perfetto 是不同的采集/分析工具,Perfetto UI 也能打开旧 Systrace 格式,并不是两个可互换的命令名。

应用启动完整图

准备时先明确启动入口、应用版本、系统 build、冷/温/热状态、编译与缓存状态、账号/网络、显示模式和温控条件。冷启动要确认目标应用进程此前不存在;从最近任务划掉不保证进程已退出,进程冷启动也不等于文件缓存、安装状态和数据全部“冷”。

  1. 先采一份基础系统 Trace。 Android 9(API 28)起可使用开发者选项中的 System Tracing;Android 10 起该工具保存 Perfetto 格式,见官方采集说明。按设备可用项启用调度、频率、Input、Activity/Window Manager、View/图形和 Binder 事件,覆盖点击前到关键内容就绪之后。没有某条轨道,要先检查采集配置、权限和丢事件,不能当作耗时为零。
  2. 需要看 IRQ 时再加入相关事件。 旧 Systrace 的 irq 类别是否可用取决于设备和权限。先查看本机 systrace.py --help 与 --list-categories,再选择类别;类别列表随设备变化,不宜照搬一整串参数。新采集可按 Perfetto 系统跟踪教程配置,IRQ handler 与线程化中断的调度记录应结合看。
  3. IPC 调试是可选增强。 am trace-ipc 启用的是 Binder 调用跟踪,不是给全部 native/Java IPC 自动恢复函数名。AOSP Android 11 的 ActivityManagerService 检查调用权限,并在非 debuggable 系统上过滤非 debuggable 应用;不能简化成“必须 root”或“root 后一定全有”。Android 9 的 Java Binder 跟踪可记录调用栈和部分方法/事务名称,采集额外栈信息也有开销。需要时先启动,复现结束后停止并导出:
1
2
3
4
adb shell am trace-ipc start
# Reproduce the launch while the system trace is recording.
adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc-trace.txt
adb pull /data/local/tmp/ipc-trace.txt

导出的文件是 IPC 调试信息,不是系统 Trace 的替代品。命令失败或目标进程不符合条件时,保留基础 Binder 事件,不能把缺少方法名解释为没有调用。权限依据见 Android 11 ActivityManagerService,旧 Java 跟踪实现见 Android 9 Binder。

如果可以重新编译应用,可以在关键初始化阶段手动添加 Trace 区间,或使用 TraceFix 做字节码插桩。它只覆盖选中且可插桩的方法,不包括系统 Framework 和 native 方法,也不意味着每一个函数必有记录。接入前核对插件与 AGP/Gradle/JDK 的版本矩阵;定位时缩小插桩范围,最终耗时回归要控制插桩和调试开销。

没有该插件时,仍可看到已有的 Framework、系统和应用自定义 Trace 点:

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

2. Android App 冷启动流程分析

本文以 从桌面冷启动 Android App 为例,沿着触摸输入、启动请求、进程初始化到关键界面内容就绪的链路分析。下面是参与的模块,不是六段可以直接相加的串行耗时;启动动画与应用初始化往往重叠:

  1. 触摸屏中断处理阶段
  2. InputReader 和 InputDispatcher 处理 input 事件阶段
  3. Launcher 处理 input 事件阶段
  4. SystemServer 处理启动事件
  5. 启动动画
  6. 应用启动和自身逻辑阶段

先约定测量起点:物理接触、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 解读 这篇文章

InputReader 和 InputDispatcher

示例按一次点击标出了 Down、若干 Move 和 Up。点击可以没有 Move,也可能被 Cancel、移动距离或长按逻辑取消;这些动作类型应由事件详情确认,不能只看几个相似的函数区间。

Launcher 的窗口建立输入通道后,InputDispatcher 根据触摸位置、窗口触摸区域、层级和输入策略选择目标;不是进程创建或“前台可见”就保证收到所有输入。input 事件在 SystemServer 和 App 中的流转可以参考 Android Systrace 基础知识 - Input 解读 ,这里把核心的两张图放上来

2.2.1 Input 事件在 SystemServer 中流转

看下图即可,如果要看更详细的,可以查看 Android Systrace 基础知识 - Input 解读
Input 事件在 SystemServer 中流转

2.2.2 Input 事件在 Launcher 进程流转

看下图即可,如果要看更详细的,可以查看 Android Systrace 基础知识 - Input 解读

这两张旧图来自魅族 Launcher,用于解释队列,不是上面 WanAndroid 示例的同一份记录。wq 减少通常表示 Dispatcher 收到处理完成的确认,不表示画面已经呈现。第二张图的 aq 标注也要修正:在 Android 10 ViewRootImpl 中,pending 计数是在取出事件、调用 deliverInputEvent 之前 减少,归零不能证明处理已完成。

2.3 Launcher 进程处理 Input 事件阶段

Launcher 的处理可以分开观察两个区间:

  1. Down 到按压反馈帧:例如图标变灰或缩小。状态更新和该帧实际呈现不是同一时刻。
  2. Up/点击判定到发出启动请求:这段更适合检查 Launcher 自身延迟。若从 Down 或首个反馈帧开始计时,还会包含用户按住图标的时间,不能全部算 Launcher 处理慢。

另外提一下,滑动桌面到桌面第一帧响应时间(这个指的是滑动桌面的场景,左右滑动桌面的时候,用高速相机拍摄,从手指动开始,到桌面动的第一帧的时间)也是一个很重要的响应速度指标,部分厂商也会在这方面做优化,感兴趣的可以自己试试主流厂商的桌面滑动场景。比较不同设备时,还需对齐刷新率、手势速度、内容和测量起终点;仅对比 Trace 不能替代物理触摸到画面变化的测量

通常的图标点击在 Up 后通过点击判定,再发出启动 Activity 的请求。Android 10 起主要由 SystemServer 中的 ActivityTaskManagerService(ATMS)处理 Activity/Task,AMS 等模块参与进程管理;下图里的代理名也是 IActivityTaskManager。通知或深链启动不一定经过 Launcher。

A9Y3FkDNSFlPuho5

这一阶段有助于区分应用启动前的系统/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 区间。

fork 新进程

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,可按下面三个里程碑分段。它不是所有应用都必须经历的结构;“界面稳定”也不够精确,应写明首屏哪些内容可见、哪些操作可用,以及错误/空数据状态如何判定。

  1. 应用进程启动到 SplashActivity 第一帧显示(部分 App 没有 SplashActivity,所以可以省略这一步,直接到进程启动到 主 Activity 第一帧显示 )
  2. SplashActivity 第一帧显示到主 Activity 第一帧显示
  3. 主 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 客户端)

OIG57AudX193jD3u

End

本文重点放在了如何在 Systrace 中展示 App 的完整冷启动流程,方便大家在做 App 的启动优化的时候,可以通过 Systrace 来快速定位到启动瓶颈,也方便进行竞品的对比和分析,

  1. 至于如何分析,可以查看 Systrace 响应速度实战 1 :了解响应速度原理 的分析套路部分
  2. 至于如何优化,可以查看 Android App 启动优化全记录 这篇文章,这里就不再重复了。不过随着技术的发展,有些优化手段会消失,而会有新的优化手段冒出来,我也会对这篇文章进行维护,如果大家发现新的优化技术,麻烦博客留言或者加微信(553000664)通知我,我来进行调研和更新

系列文章

  1. Systrace 响应速度实战 1 :了解响应速度原理
  2. Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例
  3. Systrace 响应速度实战 3 :响应速度延伸知识
  4. Systrace 基础知识系列-放个链接在这里方便大家直接点过去

参考文章

  1. Android 应用启动全流程分析
  2. 探究 | App Startup 真的能减少启动耗时吗
  3. Jetpack 新成员,App Startup 一篇就懂
  4. App Startup
  5. Android App 启动优化全记录
  6. Android application profiling

关于我 && 博客

下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!

  1. 博主个人介绍 :里面有个人的微信和微信群链接。
  2. 本博客内容导航 :个人博客内容的一个导航。
  3. 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
  4. Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。

一个人可以走的更快 , 一群人可以走的更远

微信扫一扫

CATALOG
  1. 1. 本文目录
  • 1. 准备工作
  • 2. Android App 冷启动流程分析
    1. 1. 2.1 触摸屏中断处理阶段
    2. 2. 2.2 InputReader 和 InputDispatcher 处理 Input 事件阶段
      1. 2.1. 2.2.1 Input 事件在 SystemServer 中流转
      2. 2.2. 2.2.2 Input 事件在 Launcher 进程流转
    3. 3. 2.3 Launcher 进程处理 Input 事件阶段
    4. 4. 2.4 SystemServer 处理 StartActivity 阶段
      1. 4.1. 处理启动命令
      2. 4.2. Zygote 64 位进程执行 Fork 操作
      3. 4.3. 对应的 App 进程出现
    5. 5. 2.5 应用进程启动阶段
      1. 5.1. 应用进程启动到 SplashActivity 第一帧显示
      2. 5.2. SplashActivity 第一帧显示到主 Activity 第一帧显示
      3. 5.3. 主 Activity 第一帧显示到界面完全加载并显示
  • End
  • 系列文章
  • 参考文章
  • 关于我 && 博客