1. Perfetto 是什么?
1.1 Perfetto是谷歌贡献的开源卡顿性能调优工具
官网如下:perfetto.dev/
官方使用文档:perfetto.dev/docs/
UI网页的链接(重要): [Perfetto UI](https://ui.perfetto.dev/)
1、新一代性能分析工具,systrace 支持的功能 perfetto都支持
2、Perfetto 相比 Systrace最大的改进是可以支持长时间数据抓取,这得益于它有一个可在后台运行的服务
3、在可视化网页上,可以看到各种二次处理数据,可以执行 SQL查询命令,
4、核心原理和 systrace 一样
Perfetto是很好的性能分析工具,发展很快。它不仅可以看systrace,也可以将logcat也一起提取出来,方便与其他信息放在同一个时间轴上去分析。它也可以分析memory问题,如ion 和dma_buf 泄露。Perfetto是一个高效的性能分析工具,可以提高程序员分析问题的效率,希望大家可以掌握。
Perfetto 可以收集多种类型的数据,如 CPU 使用率、内存使用情况、网络流量等。它还可以跟踪应用程序的事件,如启动时间、触摸事件等。
核心定义 :Perfetto 是 Google 开发的一个开源、跨平台的平台级性能检测框架。它不仅是 Android 系统的下一代追踪工具,也支持 Linux 和 Chrome。
核心目标:提供一种高效、强大且统一的方法来收集和分析系统和应用级的性能数据,帮助开发者理解软件在硬件上的行为。
关键特性:
- 统一替代品:旨在长期替代 Android 之前的工具链(如 Systrace、simpleperf 的部分功能)。
- 强大的数据源 :可以收集极其丰富的数据,包括但不限于:
- CPU:调度、频率、空闲状态、堆栈追踪(用户态和内核态)。
- GPU:GPU 频率、工作负载。
- 内存 :内存分配(
libc.malloc
)、内存状态。 - 图形 :SurfaceFlinger、App 渲染帧(
Choreographer
)、VSYNC。 - 系统服务:ActivityManager、Binder 调用、输入事件。
- 应用层 :自定义 Trace 点(
Trace.beginSection()
)、Kotlin 协程。
- 可扩展性:支持通过 Probes 自定义数据源。
- SQL 分析引擎:内置 SQL 引擎,允许对追踪数据执行复杂的查询,进行量化分析。
- 长时间录制:支持后台服务长时间(数小时)录制,适用于分析耗电、间歇性卡顿等问题。
1.2 非常好的一个作用
而 Perfetto 工具就提供了这样的一个上帝视角,通过上帝视角我们可以看到 Android 系统在运行时的各个细节,比如
- Input 事件是怎么流转的
- 你正在使用的 App 的每一帧是怎么从 App 产生到上屏的
- CPU 的实时频率、负载、摆核、唤醒等

2. 如何录制 .trace 文件
主要有三种主流方法:
方法一:使用 Perfetto 命令行(最强大、最灵活)
录制的参数配置Recording settings

录制的命令参考和运行

这是推荐给开发者的主要方式,尤其适合自动化脚本和深度定制。
bash
# 基本命令模板
adb shell perfetto \
-c - --txt \
-o /data/misc/perfetto-traces/trace.perfetto-trace \
<<EOF
# 配置内容开始
duration_ms: 10000 # 录制10秒
buffers: {
size_kb: 8960
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "gfx"
ftrace_events: "binder"
ftrace_events: "power/cpu_frequency"
# ... 更多事件
}
}
}
# ... 更多数据源配置
EOF
# 将文件从设备拉取到电脑
adb pull /data/misc/perfetto-traces/trace.perfetto-trace .
方法二:使用设备内置的"系统跟踪"应用(最方便)
- 在手机的开发者选项中,找到"系统跟踪"或"System Tracing"并开启。
- 下拉状态栏,会出现一个"录制跟踪记录"的磁贴。
- 点击开始录制,操作App,完成后再次点击停止。
- Trace 文件会保存在设备上,并通过通知分享或导出。
方法三:使用 Web UI (ui.perfetto.dev) 录制(最直观,我不推荐)
- 用 Chrome 浏览器打开 ui.perfetto.dev。
- 点击 "Record new trace"。
- 连接你的设备,在 "Recording settings" 中勾选需要录制的类别(如 CPU、GPU、Memory、Graphics)。
- 设置录制时长,点击 "Start recording"。
- 在设备上操作你的 App,录制结束后文件会自动下载并在浏览器中打开。
备注.我试了下,用这个工具录制的后缀是prtrace,没用,用小米手机才行
3. Perfetto 界面的基本操作
Perfetto 和Systrace 文件如何相互转化
3.1 通过 Perfetto UI 导入操作
3.1.1 访问 Perfetto UI
打开浏览器,访问 Perfetto UI 网站。
3. 1. 2 上传 HTML 文件
点击左上角的 "Open trace file" 按钮。
选择你的 systrace.html
文件,直接拖放或从文件管理器中选择
3.2 界面操作
导航 : * 放大/缩小 :
W
/ S
或鼠标滚轮。 * 左右移动 :A
/ D
或按住 Shift
并滚动鼠标滚轮。 * 重置视图 :R
。
- 选择与查看 :
- 点击Slice :在下方详情面板(
Current Selection
)中查看其详细信息(名称、耗时、参数等)。 - 区域选择:鼠标左键拖动,可以高亮一个区域,并显示该区域的总时间。
- 点击Slice :在下方详情面板(
- 标记(Marking) :
- 临时标记 :选中一个Slice后按
M
,会创建一条临时的竖线,方便对齐查看其他轨道。 - 永久标记 :选中一个Slice后按
Shift
+M
,会创建一个带注释的永久标记,可用于记录分析点。
- 临时标记 :选中一个Slice后按
- 置顶和标记
m 是临时 Mark 一段区域(与 Systrace 一样), 用来上下看时间、看其他进程信息等。临时的意思就是你如果按 m 去 mark 另外一个区域,那么上一个用 m mark 出来的 Mark 区域就会消失。退出临时选中:esc ,或者选择其他的 Slice 按 m,当前这个 Slice 的选中效果就会消失
- 删除持续 Mark
- 点击你选中的那个 Slice 的最上面那个三角
- 下面选择 Tab:Current Selection
- 点击最后边的 Remove ,就可以把他 Remove 掉了
用得最多的其实就是添加标记。
当我们选中一个片段时,点击 m,就可以做一个临时的标记,当标记另一个片段以后,前一个零时标记就会取消。
选中一个片段以后,如果点击 shift + m,就会添加一个普通标记。
标记以后,会出现两个小三角:

- 搜索 :
Ctrl
+F
,可以全局搜索Trace中的文本(如线程名、函数名)。 - SQL查询 :点击右上角的
Query (SQL)
按钮,可以编写SQL语句对trace数据进行量化分析(例如,统计所有Binder调用的平均耗时)。
3.3 参数:看主线程
still capture
如果想看此时CPU 频率,左键长按在CPU Frequency上!里面有2个参数cpu by thread,cpu by process
录制的时候就要打开!

别人会显示这个cpu frquency
3.3.2).一些命令的使用会内建许多 SQL 表和图,方便使用 SQL 语句进行查询,比如下面这几个查询,就是非常实用
非常有用的SQL语句

会发现同一个线程会有两行。其实这两行的目的是不一样的,第一行一般记录线程的运行状态,第二行记录线程的trace。
Thread State的Waker 和Woken by是一个东西么
3.4 .重要的参数:
触摸(deliverInputEvent),cpu,日志查看,线程状态查看,任务 slice detail
链路是怎么切换的?
确认有 Android App Startups 标识(没有的话重新录制一遍)
如何通过包名找到?
找到包名后,点击,展开看里面的信息
打开trace文件后的效果,红框部分就是配置文件中自行配置的需求:
perfetto如何把工作区的数量精减
SurfaceFlinger 怎么没有! crol+F搜索
VSYNC-app 也在SurfaceFlinger 里面
4. Perfetto 核心指标
4.1 CPU调度信息
点击cpu就知道了!

4.2 线程状态信息
线程状态查看
深绿色 : 运行中(Running)
浅绿色 : 可运行(Runnable)
白色/无色: 睡眠中(Sleeping)
4.2.3 线程唤醒信息:
点击 Runnable 状态
主线程的Runnable和Acturel的线程的runnable
4.3 帧率信息
4.4 触摸事件
触摸事件: deliverInputEvent
值得注意的是Perfetto默认不监控binder和所有app的trace,在这里我们需要改动一下配置开启监控binder和所有app的trace。然后我们也可以自定义duration_ms字段来控制profile的时间长短。我工作的时候需要监控接听电话的performance,所以我一般设置成30秒。

滑动下方的android log,可以看到有一根竖线,对应到trace的tag,日志和trace tag的一一对应,是不是很牛逼的功能。
python
Trace.beginAsyncSection("test1", 1);
slice 代表了一段代码的执行过程,起于 Trace.traceBegin \ ,终于 Trace.traceEnd \ ATRACE_END

5. Perfetto 分析卡顿的具体操作步骤
这是一个系统化的分析流程,遵循"从宏观到微观"的原则:
-
定位问题帧(找到"罪证"):
- 首先寻找
Expected Timeline
和Actual Timeline
。 - 找到那些
Actual
远远落后于Expected
的帧,它们通常被标记为红色(严重丢帧) 或黄色。
- 首先寻找
-
确定责任方(App vs. System):
- 观察问题帧对应的时间点:
- 如果
SurfaceFlinger
轨道繁忙(composite
操作耗时很长),可能是系统合成或GPU瓶颈。 - 如果 App 的
UI Thread
或RenderThread
的帧周期(Choreographer#doFrame
)超时,问题在App自身。绝大多数卡顿属于此类。
- 如果
- 观察问题帧对应的时间点:
-
剖析问题线程(根因分析):
- 点击展开App的
UI Thread
,找到超时的那个doFrame
slice。 - 分析其内部结构 :通常分为
input
、animation
、traversal
(measure
、layout
、draw
)阶段。看哪个阶段耗时最长。 - 查看线程状态 :
- 深绿色 (Running) :线程正在执行。点击耗时最长的深绿色Slice ,查看详情,往往能找到具体的函数名(如
RecyclerView.onLayout
)。 - 浅绿色 (Runnable) :线程可运行但未获CPU调度。这是 CPU 抢占 的标志。需检查同期CPU负载。
- 白色 (Sleeping) :线程被阻塞。查看
Waker
信息,看它在等谁(通常是等Binder调用返回或锁)。
- 深绿色 (Running) :线程正在执行。点击耗时最长的深绿色Slice ,查看详情,往往能找到具体的函数名(如
- 点击展开App的
-
关联系统资源(寻找系统瓶颈):
- CPU:检查同期所有CPU核心的频率和利用率。是否被其他进程占满?你的应用线程是否被调度到了小核上?
- Binder :查看
binder_transaction
轨道,是否有频繁或耗时的跨进程调用? - IO:检查是否有密集的文件读写操作。
-
使用SQL进行量化(可选,用于证明):
- 例如,可以写SQL查询所有
doFrame
的平均耗时,或者统计主线程Sleeping
的总时间,为你的优化提供数据支撑。
- 例如,可以写SQL查询所有
5.2 总结:
卡顿的原因
你的 App 卡顿不一定是你 App 的问题,也有可能是系统的问题,不过不管怎么说,首先要会分析卡顿问题。
APP的原因
1).主线程卡主
2).绘制
系统问题
1).OpenDexFilesFromOat耗时
现象:slow_start_reason出现"Main Thread - Time spent in OpenDexFilesFromOat*",表示启动阶段,花费了较多时间在读取dex文件上。
2).摆核,运行在非大核
3).Binder, 跨进程导致
SystemServer 就会由于 Binder 通信和锁竞争,导致系统或者 App 卡
分析步骤:
-
0.Android App Startups 标识
-
1.Expected Timeline 和 Actual Timeline
-
2.UI Thread和Render Thread
主线程中的Choreographer的时间,正常是16.6
其实这两行的目的是不一样的,第一行一般记录线程的运行状态,第二行记录线程的trace。
-
3.SurfaceFlinger线程---VSYNC-app
-
4.线程的运行状态,是被那个唤醒的
-
5.cpu
-
6.Input 事件-->(deliverInputEvent)
-
7.动画--------->animator
-
8..Perfetto 上查看 Log
log一直是 空白的
快速查看 App 执行超时
由于 Android 多 Buffer 机制的存在,App 执行超时不一定会卡顿,但是超时是需要我们去关注的。
通过 Perfetto 提供给的 Expected Timeline 和 Actual Timeline 这两行,可以清楚看到执行超时的地方
我这边用
perfetto,一直抓不到 Expected Timeli 和 Actual
Timeline,config文件里也配置了。 用了android11和android14的,都抓不到,有没有解法?
我实际看这2个,颜色,我感觉一点都不准确!
6. 实战:Perfetto分析卡顿案例
6.1 分析 RecyclerView 卡顿
代码案例,和上一篇SystemTrace是一样的
java
public class PerformanceAdapter extends RecyclerView.Adapter<PerformanceAdapter.ViewHolder> {
private final List<String> items;
private final BindTimeListener bindTimeListener;
private long totalBindTime = 0;
private int bindCount = 0;
public interface BindTimeListener {
void onBindTime(long bindTime);
}
public PerformanceAdapter(List<String> items, BindTimeListener listener) {
this.items = items;
this.bindTimeListener = listener;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public final TextView titleText;
public final TextView subtitleText;
public ViewHolder(View view) {
super(view);
titleText = view.findViewById(R.id.titleText);
subtitleText = view.findViewById(R.id.subtitleText);
}
}
@Override
public ViewHolder onCreateViewHolder( ViewGroup parent, int viewType) {
// 模拟耗时操作
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_layout, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder( ViewHolder holder, int position) {
long startTime = System.currentTimeMillis();
// 模拟复杂数据绑定
holder.titleText.setText("Item " + items.get(position));
holder.subtitleText.setText("This is a long subtitle for item " + items.get(position) +
" that will cause text measurement to take more time");
// 故意制造性能问题 - 在主线程进行耗时操作
if (position % 5 == 0) {
simulateHeavyWork();
}
// 随机改变颜色增加布局复杂度
if (position % 3 == 0) {
holder.titleText.setTextColor(Color.RED);
} else {
holder.titleText.setTextColor(Color.BLACK);
}
// 记录绑定耗时
long bindTime = System.currentTimeMillis() - startTime;
totalBindTime += bindTime;
bindCount++;
// 回调报告绑定时间
if (bindTimeListener != null) {
bindTimeListener.onBindTime(bindTime);
}
}
@Override
public int getItemCount() {
return items.size();
}
public double getAverageBindTime() {
return bindCount > 0 ? (double) totalBindTime / bindCount : 0;
}
private void simulateHeavyWork() {
// 1. 主线程耗时计算
Random random = new Random();
long sum = 0;
for (int i = 0; i <= 10000000; i++) {
sum += random.nextLong();
}
}
}
RecyclerView 卡顿通常发生在 traversal
阶段,尤其是 layout
和 draw
。
分析思路和步骤:
- 录制场景:在 RecyclerView 快速滑动时录制 Trace。
- 定位卡顿帧:找到滑动过程中出现的红色/黄色帧。
- 深入
doFrame
:展开卡顿帧对应的UI Thread
中的doFrame
,重点关注traversal
下的performLayout
和performDraw
。 - 寻找罪魁祸首 :
- 布局 (
onLayout
) :- 在
performLayout
下方,你很可能会看到一个非常长的RecyclerView.onLayout
slice。 - 点击它,展开其调用树。你会看到无数个
LinearLayoutManager.layoutChunk
和RecyclerView$ViewHolder
的 slice。这表明是在布局子项。 - 根因 :Item 布局过于复杂、
getView
/onBindViewHolder
中有繁重操作(同步网络请求、图片解码、复杂计算)。
- 在
- 绘制 (
onDraw
) :- 如果在
performDraw
阶段耗时,可能是RecyclerView
或其子项onDraw
中有昂贵操作(如圆角、阴影、canvas.saveLayer
)。
- 如果在
- 滑动时的无效化 :
- 注意观察是否有非预期的
invalidate
或requestLayout
调用,导致布局和绘制不断触发。
- 注意观察是否有非预期的
- 布局 (
- 解决方案 :
- 优化 ItemView :简化布局层级,使用
ConstraintLayout
。 - 优化
onBindViewHolder
:只做最简单的数据绑定,避免任何耗时操作。预加载、缓存复杂计算结果。 - 预加载和缓存:对于图片,使用成熟的库(Glide、Coil)并进行预加载。
- 减少视图无效化:避免在滑动时改变视图状态(如不断变化的背景)。
- 优化 ItemView :简化布局层级,使用
6.1.2 recyclewview的item背景:(因为很多图片绘制)
oncreate方法:是没有复用的时候,会执行!


- recyclewview的sleep导致的:
会一直回调rvBind方法执行
3.draw 方法里面一直循环
显示draw方法耗时
布局问题的案例
4.主线程耗时的案例(看不出来)
有丢帧,但是不知道是哪里导致的!
用斐波拉契数列,然后去点击
scss
private int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
6.2 Perfetto 案例分析1 sleep(主线程耗时)
第一层:running,infate,具体干的事情和耗时分析!
先看主线的整体耗时,然后看里面每一个项的耗时情况!

如果定位到具体的某一行代码?
触发APP_SCOUT_HANG事件(主线程阻塞或死循环)。以下是可能原因及建议解决方案:
用斐波拉数列,不能找到具体的方法!
用thread.sleep()也砍不到具体的方法
但是绘制的时候,能有具体的方法!

具体的案例: inflate!
如果是sleep,是什么样的效果

6.2.2 整体绘制查看:


这张图对应的工作流程如下
- 主线程处于 Sleep 状态,等待 Vsync 信号
- Vsync 信号到来,主线程被唤醒,Choreographer 回调 FrameDisplayEventReceiver.onVsync 开始一帧的绘制
- 处理 App 这一帧的 Input 事件(如果有的话)
- 处理 App 这一帧的 Animation 事件(如果有的话)
- 处理 App 这一帧的 Traversal 事件(如果有的话)
- 主线程与渲染线程同步渲染数据,同步结束后,主线程结束一帧的绘制,可以继续处理下一个 Message(如果有的话,IdleHandler 如果不为空,这时候也会触发处理),或者进入 Sleep 状态等待下一个 Vsync
- 渲染线程首先需要从 BufferQueue 里面取一个 Buffer(dequeueBuffer) , 进行数据处理之后,调用 OpenGL 相关的函数,真正地进行渲染操作,然后将这个渲染好的 Buffer 还给 BufferQueue (queueBuffer) , SurfaceFlinger 在 Vsync-SF 到了之后,将所有准备好的 Buffer 取出进行合成(这个流程在讲 SurfaceFlinger 的时候会提到)
启动:

frame的帧绘制

7. 使用 Perfetto SDK 进行自定义追踪
如果你需要追踪应用中的特定部分,可以使用 Perfetto SDK 来添加自定义追踪点。
- 将 Perfetto SDK 添加到应用中: 通过在 build.gradle 文件中添加依赖项,将 SDK 集成到项目中:
arduino
dependencies {
implementation 'com.google.perfetto:trace:VERSION'
}
- 在代码中进行追踪: 添加 SDK 后,你可以使用 Trace API 来创建追踪点。例如:
scss
Trace.beginSection("custom_event");
// 执行一些你想测量的操作
Trace.endSection();
这些追踪点会出现在 Perfetto 的追踪输出中,让你清楚地看到特定操作的执行时间。
我们可以通过自定义插件的方法,去插桩我们的Trace代码。
8. Perfetto 和 Systrace 的区别和联系
1).Systrace 做不到的(超过 300M 以上时可能会崩溃、可能会超卡)
2).功能比Systrace 更强
3).2022年Perfetto 开始替代
特性 | Perfetto | Systrace |
---|---|---|
定位 | 下一代 、统一的平台级追踪框架 | 上一代 、基于 ftrace 的工具 |
录制能力 | 强大。支持长时间、后台录制,数据源极其丰富。 | 较弱。通常录制短时间(5-10秒),数据源有限。 |
文件格式 | .perfetto-trace (ProtoBuf 格式) |
.html (其实是一个封装了JSON的HTML) |
分析能力 | 极强 。内置SQL查询引擎,支持复杂的量化分析。 | 基础。只能进行可视化的、定性的分析。 |
可视化界面 | 现代化的 Web UI (ui.perfetto.dev),功能持续更新。 | 独立的 Python 脚本生成的离线 HTML 页面,已停止更新。 |
联系 | Perfetto 完全兼容并包含了 Systrace 的功能 。你可以用 Perfetto 打开 .html 格式的 Systrace 文件。Systrace 可以看作是 Perfetto 的一个子集 和前身。 |
结论 :Perfetto 是 Systrace 的绝对上位替代。Google 已停止更新 Systrace,所有新的开发和优化都集中在 Perfetto 上。新项目和学习都应该直接使用 Perfetto。
9. Perfetto 总结
核心价值 : Perfetto 提供了从应用层 到内核层 的全栈(Full-Stack) 观测能力,将一个复杂的系统变成了一个"透明盒子"。它将性能分析从"凭经验猜"变成了"看数据说话"。
学习建议:
- 掌握流程 :牢牢记住 "定位帧 -> 定责 -> 剖析线程 -> 关联系统" 的分析流程。
- 理解状态 :深刻理解线程的
Running
、Runnable
、Sleeping
三种状态的含义,这是分析问题的基石。 - 善用工具 :积极使用
Waker
信息追溯阻塞链,使用 SQL 进行数据量化,使用自定义 Trace 点 来定位自家代码。 - 实践为王:多录、多看、多分析。从简单的案例(如主线程sleep)开始,逐步分析复杂的真实业务场景。
Perfetto 是每一个追求极致体验的 Android 开发者必须掌握的核心竞争力。
RecyclerView卡顿案例地址: github.com/pengcaihua1...