4. Android <卡顿四> 卡顿性能第二代工具 Perfetto 精准定位 Android 性能瓶颈 (工具使用)

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 系统在运行时的各个细节,比如

  1. Input 事件是怎么流转的
  1. 你正在使用的 App 的每一帧是怎么从 App 产生到上屏的
  1. 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 .
方法二:使用设备内置的"系统跟踪"应用(最方便)
  1. 在手机的开发者选项中,找到"系统跟踪"或"System Tracing"并开启。
  2. 下拉状态栏,会出现一个"录制跟踪记录"的磁贴。
  3. 点击开始录制,操作App,完成后再次点击停止。
  4. Trace 文件会保存在设备上,并通过通知分享或导出。
方法三:使用 Web UI (ui.perfetto.dev) 录制(最直观,我不推荐)
  1. 用 Chrome 浏览器打开 ui.perfetto.dev
  2. 点击 "Record new trace"
  3. 连接你的设备,在 "Recording settings" 中勾选需要录制的类别(如 CPU、GPU、Memory、Graphics)。
  4. 设置录制时长,点击 "Start recording"
  5. 在设备上操作你的 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)中查看其详细信息(名称、耗时、参数等)。
    • 区域选择:鼠标左键拖动,可以高亮一个区域,并显示该区域的总时间。
  • 标记(Marking)
    • 临时标记 :选中一个Slice后按 M,会创建一条临时的竖线,方便对齐查看其他轨道。
    • 永久标记 :选中一个Slice后按 Shift + M,会创建一个带注释的永久标记,可用于记录分析点。
  • 置顶和标记

m 是临时 Mark 一段区域(与 Systrace 一样), 用来上下看时间、看其他进程信息等。临时的意思就是你如果按 m 去 mark 另外一个区域,那么上一个用 m mark 出来的 Mark 区域就会消失。退出临时选中:esc ,或者选择其他的 Slice 按 m,当前这个 Slice 的选中效果就会消失

  • 删除持续 Mark
  1. 点击你选中的那个 Slice 的最上面那个三角
  1. 下面选择 Tab:Current Selection
  1. 点击最后边的 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 分析卡顿的具体操作步骤

这是一个系统化的分析流程,遵循"从宏观到微观"的原则:

  1. 定位问题帧(找到"罪证")

    • 首先寻找 Expected TimelineActual Timeline
    • 找到那些 Actual 远远落后于 Expected 的帧,它们通常被标记为红色(严重丢帧) 或黄色。
  2. 确定责任方(App vs. System)

    • 观察问题帧对应的时间点:
      • 如果 SurfaceFlinger 轨道繁忙(composite 操作耗时很长),可能是系统合成或GPU瓶颈。
      • 如果 App 的 UI ThreadRenderThread 的帧周期(Choreographer#doFrame)超时,问题在App自身。绝大多数卡顿属于此类
  3. 剖析问题线程(根因分析)

    • 点击展开App的 UI Thread,找到超时的那个 doFrame slice。
    • 分析其内部结构 :通常分为 inputanimationtraversalmeasurelayoutdraw)阶段。看哪个阶段耗时最长。
    • 查看线程状态
      • 深绿色 (Running) :线程正在执行。点击耗时最长的深绿色Slice ,查看详情,往往能找到具体的函数名(如 RecyclerView.onLayout)。
      • 浅绿色 (Runnable) :线程可运行但未获CPU调度。这是 CPU 抢占 的标志。需检查同期CPU负载。
      • 白色 (Sleeping) :线程被阻塞。查看 Waker 信息,看它在等谁(通常是等Binder调用返回或锁)。
  4. 关联系统资源(寻找系统瓶颈)

    • CPU:检查同期所有CPU核心的频率和利用率。是否被其他进程占满?你的应用线程是否被调度到了小核上?
    • Binder :查看 binder_transaction 轨道,是否有频繁或耗时的跨进程调用?
    • IO:检查是否有密集的文件读写操作。
  5. 使用SQL进行量化(可选,用于证明)

    • 例如,可以写SQL查询所有 doFrame 的平均耗时,或者统计主线程 Sleeping 的总时间,为你的优化提供数据支撑。

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 阶段,尤其是 layoutdraw

分析思路和步骤

  1. 录制场景:在 RecyclerView 快速滑动时录制 Trace。
  2. 定位卡顿帧:找到滑动过程中出现的红色/黄色帧。
  3. 深入 doFrame :展开卡顿帧对应的 UI Thread 中的 doFrame,重点关注 traversal 下的 performLayoutperformDraw
  4. 寻找罪魁祸首
    • 布局 (onLayout)
      • performLayout 下方,你很可能会看到一个非常长的 RecyclerView.onLayout slice。
      • 点击它,展开其调用树。你会看到无数个 LinearLayoutManager.layoutChunkRecyclerView$ViewHolder 的 slice。这表明是在布局子项。
      • 根因 :Item 布局过于复杂、getView/onBindViewHolder 中有繁重操作(同步网络请求、图片解码、复杂计算)。
    • 绘制 (onDraw)
      • 如果在 performDraw 阶段耗时,可能是 RecyclerView 或其子项 onDraw 中有昂贵操作(如圆角、阴影、canvas.saveLayer)。
    • 滑动时的无效化
      • 注意观察是否有非预期的 invalidaterequestLayout 调用,导致布局和绘制不断触发。
  5. 解决方案
    • 优化 ItemView :简化布局层级,使用 ConstraintLayout
    • 优化 onBindViewHolder:只做最简单的数据绑定,避免任何耗时操作。预加载、缓存复杂计算结果。
    • 预加载和缓存:对于图片,使用成熟的库(Glide、Coil)并进行预加载。
    • 减少视图无效化:避免在滑动时改变视图状态(如不断变化的背景)。

6.1.2 recyclewview的item背景:(因为很多图片绘制)

oncreate方法:是没有复用的时候,会执行!

  1. 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 整体绘制查看:

这张图对应的工作流程如下

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

启动:

frame的帧绘制

7. 使用 Perfetto SDK 进行自定义追踪

如果你需要追踪应用中的特定部分,可以使用 Perfetto SDK 来添加自定义追踪点。

  1. 将 Perfetto SDK 添加到应用中: 通过在 build.gradle 文件中添加依赖项,将 SDK 集成到项目中:
arduino 复制代码
dependencies {

    implementation 'com.google.perfetto:trace:VERSION'

}
  1. 在代码中进行追踪: 添加 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) 观测能力,将一个复杂的系统变成了一个"透明盒子"。它将性能分析从"凭经验猜"变成了"看数据说话"。

学习建议

  1. 掌握流程 :牢牢记住 "定位帧 -> 定责 -> 剖析线程 -> 关联系统" 的分析流程。
  2. 理解状态 :深刻理解线程的 RunningRunnableSleeping 三种状态的含义,这是分析问题的基石。
  3. 善用工具 :积极使用 Waker 信息追溯阻塞链,使用 SQL 进行数据量化,使用自定义 Trace 点 来定位自家代码。
  4. 实践为王:多录、多看、多分析。从简单的案例(如主线程sleep)开始,逐步分析复杂的真实业务场景。

Perfetto 是每一个追求极致体验的 Android 开发者必须掌握的核心竞争力

RecyclerView卡顿案例地址: github.com/pengcaihua1...

相关推荐
布列瑟农的星空2 分钟前
大话设计模式——多应用实例下的IOC隔离
前端·后端·架构
EndingCoder6 分钟前
安装与环境搭建:准备你的 Electron 开发环境
前端·javascript·electron·前端框架
蓝银草同学22 分钟前
前端离线应用基石:深入浅出 IndexedDB 完整指南
前端·indexeddb
龙在天33 分钟前
什么是SourceMap?有什么作用?
前端
雪中何以赠君别37 分钟前
Vue 2 与 Vue 3 双向绑定 (v-model) 区别详解
前端·javascript·vue.js
林太白39 分钟前
Vue3-ElementPlus使用
前端·javascript·vue.js
Juchecar1 小时前
npm、pnpm、yarn 是什么?该用哪个?怎么用?如何迁移?
前端·node.js
CYRUS_STUDIO1 小时前
Miniconda 全攻略:优雅管理你的 Python 环境
前端·后端·python
学不动学不明白1 小时前
ECharts 为visualMap视觉映射添加自适应外边框
前端
怪可爱的地球人1 小时前
ts的高级类型
前端