3. Android <卡顿三> 卡顿性能分析工具 SystemTrace 精准定位 Android 性能瓶颈 (工具使用)

1. System Trace 是什么?

System Trace(系统跟踪) 是 Android 平台最强大的性能分析工具之一。它如同一台 "时间机器""系统级录屏",能够记录短时间内整个设备上所有软件和硬件活动的详细日志。

它的核心原理是在系统内核、Framework 层和应用层的关键位置插入探针(TracePoint)。当代码执行到这些点时,会留下一个时间戳和上下文信息。最终,所有这些点连点成线,形成一条可视化的时间轴,让我们能清晰地看到:

  • CPU:每个核心在做什么?哪个线程在运行?
  • 图形系统 :应用是如何绘制界面的?(UI Thread, RenderThread
  • 显示系统 :系统是如何合成并显示画面的?(surfaceflinger, VSYNC
  • 应用活动Activity 启动、Service 绑定、Input 事件分发。
  • 磁盘与网络 I/O:文件读写、网络请求阻塞在了哪里?

Systrace有网页版本和profiler自带的版本

Systrace 与 Perfetto

  • Systrace 是传统工具的名称,基于 Python 脚本。
  • Perfetto 是其现代化继任者,从 Android 10 成为官方首选,功能更强大。如今我们常说的 "抓 Systrace",通常指的是使用 Perfetto 的录制功能分析界面

2. 如何录制 .trace 文件?

录制 Trace 文件主要有三种方法,推荐使用第一种。

方法一:命令行录制(最强大灵活)

这是开发者最常用的方式,可以精确控制要抓取的内容。

使用 Perfetto (推荐)

  1. 准备配置文件 :创建一个 config.pbtx 文本文件。

    protobuf 复制代码
    duration_ms: 10000 # 抓取时长:10秒
    
    buffers: {
        size_kb: 63488
        fill_policy: DISCARD
    }
    
    data_sources: {
        config {
            name: "linux.ftrace"
            ftrace_config {
                ftrace_events: "sched/sched_switch"
                ftrace_events: "sched/sched_wakeup"
                ftrace_events: "gpu_scheduler/gpu_scheduler_run_job"
                atrace_categories: "gfx"    # 图形相关,必须
                atrace_categories: "input"  # 输入事件
                atrace_categories: "view"   # View 系统
                atrace_categories: "wm"     # 窗口管理
                atrace_categories: "am"     # 活动管理
                atrace_categories: "app"    # 应用层
            }
        }
    }
  2. 执行命令 :在终端执行以下命令,并立即在手机上滑动列表复现卡顿。

    bash 复制代码
    adb shell perfetto --config config.pbtx --out /data/misc/perfetto-traces/trace.pftrace
  3. 拉取文件 :抓取完成后,将文件拉到电脑上。

    bash 复制代码
    adb pull /data/misc/perfetto-traces/trace.pftrace .

方法二:Android Studio Profiler(最简单,最新版本的Studio改进了)

  1. 打开 Android Studio → View → Tool Windows → Profiler。
  2. 连接设备并启动你的应用。
  3. 在 Profiler 窗口中点击 CPU 区域。
  4. 点击 "System Trace" 按钮 → 点击录制 → 操作手机 → 停止录制。
  5. Android Studio 会自动打开并显示 trace 文件。

方法三:设备自带的开发者选项(适合无电脑场景)

  1. 在手机的 开发者选项系统跟踪 中开启跟踪。
  2. 选择跟踪配置(可选),然后开始跟踪。
  3. 操作手机复现问题。
  4. 停止跟踪,分享生成的 .perfetto-trace 文件。

3. System Trace 界面的基本操作

3.1 网页的版本

使用 Chrome 浏览器打开 chrome://tracing/,然后加载你的 trace 文件。

  • 放大/缩小
    • W / S 键 或 鼠标滚轮。
    • 双击 :放大到所选区域;双击空白处:重置视图。
  • 移动视图 :按住 鼠标右键 拖拽 或 A / D 键。
  • 选择与查看详情单击任何一个色块,下方会显示其详细信息(函数名、耗时、所属进程等)。
  • 快速定位到选中区域 :按 M 键,可以将当前选中的片段高亮并居中显示。这是跟踪线程间唤醒关系的神键!
  • 搜索 :按 Ctrl + F 可以搜索关键字(如进程名、函数名)。
  • 时间选择器:顶部有全局时间轴,可以快速定位到特定时间段。

3.2 最新Studio中的Profiler(推荐)

操作和网页版本一样,但是更加的简单

4. System Trace 如何具体的分析卡顿,具体的操作步骤

4.1 分析卡顿是一个有章可循的过程。请遵循以下 "四步分析法"

第一步:定位问题帧(找目标)

  1. 找到你应用进程的 Frames 轨道
  2. 寻找黄色或红色的帧圆圈,这明确指示了卡顿发生的位置。
  3. 放大这个坏帧所在的时间区域。

第二步:沿流水线溯源(定范围) Android 渲染一帧遵循一个固定的流水线,我们的分析就沿着它展开: VSYNC-app -> App UI Thread -> App RenderThread -> SurfaceFlinger -> VSYNC-sf -> Screen

  1. 检查 VSYNC :找到 vsync-app 信号,它是绘制的起点。

  2. 检查 UI Thread

    • 找到你的应用进程,展开并找到 UI Thread(或 main)。
    • 查看在两个 vsync-app 信号之间,UI Thread 是否在执行 Choreographer#doFrame
    • 如果 doFrame 的色块非常长,超过了 16.6ms,问题就在这里!
    • 点击该 doFrame 块,查看其子项目。是 measure/layout 慢,还是 draw 慢?或者是 ListView.bin/RV.onBind
  3. 检查 RenderThread

    • 如果 UI Thread 很快,问题可能出在渲染。
    • 找到你应用的 RenderThread
    • 查看它的 drawFrame 是否耗时过长。常见原因是复杂的阴影、圆角、Canvas 操作等。

第三步:检查系统资源(排外因)

  • 看 CPU :查看 CPU 轨道,确认当时 CPU 频率是否足够。查看 UI Thread 的状态,如果它长时间处于蓝色 (Runnable),表示它ready了但抢不到CPU,可能是其他线程占满了CPU。

第四步:使用 Alerts 面板(抓提示)

  • 在 Perfetto 界面中,有一个 Alerts 面板。它会自动分析整个 Trace 文件并列出所有潜在问题,如 Janky frame, Expensive measure/layout pass直接点击 Alert,它会带你定位到问题发生的位置! 这是最高效的起点。

4.2 总结: 从应用的角度分析: 核心分析流程:从"卡顿"到"代码行"

看帧的颜色(红色),主线程,火焰图, 系统的卡顿再看渲染线程

如需检测卡顿情况,请按以下步骤操作:

  1. 在 Android Studio 中,依次选择 View > Tool Windows > Profiler ,或点击工具栏中的 Profile 图标

    如果 Select Deployment Target 对话框显示提示,请选择要将您的应用部署到哪个设备以进行性能分析。如果您已通过 USB 连接设备但系统未列出该设备,请确保您已启用 USB 调试

  2. 点击 CPU 时间轴上的任意位置以打开 CPU 性能分析器。

  3. 从 CPU 性能分析器的配置菜单中选择 System Trace ,然后点击 Record 。完成与应用的交互后,点击 Stop

  4. 您应该会在 Display 下方看到 Janky frames 轨道。默认情况下,性能分析器只会将卡顿帧显示为有待调查的候选对象。在每个卡顿帧中,红色部分突出显示了相应帧超出其渲染截止时间的时长。

5.发现卡顿帧后,点击该帧;可根据需要按 M 键调整缩放程度以聚焦到所选帧。相关事件会在以下线程中突出显示:主线程、RenderThreadGPU completion

6.通过选中或取消选中 All FramesLifecycle 复选框,您可以根据需要查看所有帧或呈现时间的细分数据

第一步:找到"案发现场"------ 识别卡顿帧 (Jank Frame)

这是整个分析的起点。你必须先确定哪个时间点发生了卡顿

  1. 查看 Display 时间轴

    • 位于分析界面的最上方。
    • 每个竖直的条代表一个帧 (Frame)
    • 理想情况:每帧渲染时间 < 16.6ms (60Hz),且能对齐 VSync 信号。
    • 卡顿表现 :你会看到一个明显的 红色警告图标 (⚠️) 或文字标注 "Missed VSync""Jank" 。这就是"案发现场"!
    • 操作 :用鼠标点击 这个被标记为 Jank 的帧。系统通常会自动将时间轴的视图居中到这个帧的时间点。

第二步:锁定"嫌疑人"------ 审查主线程 (main Thread) 活动

卡顿通常是因为主线程在关键的 16.6ms 内没有完成工作。现在,我们检查 main 线程在"案发时"在做什么。

  1. 定位 main 线程

    • Threads 时间轴区域,找到你的应用包名(如 com.yourapp)。
    • 展开它,找到名为 main 的线程。
  2. 时间对齐

    • 确保时间轴的视图已经对准了你选中的那个 Jank 帧。
  3. 观察线程状态

    • 绿色 (Running) :正在 CPU 上执行代码。如果在 Jank 帧期间,main 线程有一段很长的绿色块,这几乎可以肯定问题就出在这段代码里。
    • 黄色 (Uninterruptible Sleep) :等待 I/O(磁盘、网络)。这也很可能是问题根源(同步 I/O 阻塞了 UI)。
    • 灰色 (Sleeping) :空闲。这说明主线程没在做事,卡顿可能由其他原因引起(如 GPU 渲染慢、系统调度问题)。
  4. 初步判断

    • 如果 main 线程在 Jank 帧期间是长时间绿色 → 问题:CPU 密集型任务阻塞主线程
    • 如果 main 线程在 Jank 帧期间是长时间黄色 → 问题:同步 I/O 操作阻塞主线程
    • 如果 main 线程大部分时间是灰色 → 问题可能在 RenderThreadGPU 或系统层面。

第三步:提取"指纹"------ 使用 Analysis 窗格定位具体方法

这是最关键的一步,也是最强大的功能。它能告诉你绿色/黄色块里到底在执行什么代码。

  1. 选择"作案时间"

    • 用鼠标在时间轴上拖拽 ,精确选择 main 线程上那个导致 Jank 的长绿色或黄色块。选得越精确,分析结果越准确。
  2. 查看 Analysis 窗格

    • 选择时间范围后,下方的 Analysis 窗格会实时更新,显示该时间段内的所有 CPU 活动。
  3. 切换到 Flame Chart (火焰图) - 推荐首选

    • 原理 :火焰图将所有具有相同调用栈的采样点合并成一个"火焰"形状。火焰的宽度代表该方法/函数占用的 CPU 时间。越宽,耗时越长。

    • 如何阅读

      • 从上往下读:最顶层是当前正在执行的函数。它的"父母"是调用它的函数,它的"孩子"是它调用的函数。
      • 找最宽的块 :在 main 线程的火焰图中,从顶部开始,寻找最宽的"火焰块"。这些就是耗时最长的"热点"函数。
      • 示例 :你可能会看到一个非常宽的块,名字是 com.yourapp.ui.MainActivity.loadDataFromNetwork()。这说明这个方法是主要的 CPU 消耗者。
  4. 切换到 Top Down (自上而下) - 辅助分析

    • 原理 :树形结构,根节点是 main 线程。

    • 如何阅读

      • 展开节点,查看方法调用层级。
      • 关注 Total CPU Time 列。这个时间包括了该方法自身及其所有子调用的总时间。Thread CPU Time 是方法自身执行的时间。
      • 找到 Total CPU Time 占比很高的节点,这就是性能瓶颈所在。
  5. 交叉验证

    • Flame Chart 中找到可疑方法后,可以在 Top Down 视图中找到它,查看其完整的调用路径,确认它是在什么场景下被调用的。

第四步:直击"犯罪现场"------ 跳转到源代码

当你在 Flame ChartTop Down 视图中锁定了一个可疑的方法后,就可以直接跳到代码了。

  1. 右键点击方法名 :在 Analysis 窗格中,找到你怀疑的那个方法(如 loadDataFromNetwork)。
  2. 选择 Jump to Source
  3. 结果 :Android Studio 会自动打开对应的 Java/Kotlin 源文件 ,并将光标定位到该方法的定义处,甚至可能高亮具体的行号。

至此,你已经完成了从"用户感知卡顿"到"定位具体代码行"的完整闭环!

因为系统卡顿也是存在的,如果分析系统卡顿,就要看cpu,渲染线程,surfaceFling.

5. System Trace实战:分析 RecyclerView 卡顿

场景:滑动 RecyclerView 时明显卡顿, 主线程做了耗时的计算。

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();
        }
    }
}

耗时出问题的地方:

ini 复制代码
  private void simulateHeavyWork() {
        // 1. 主线程耗时计算
        Random random = new Random();
        long sum = 0;
        for (int i = 0; i <= 10000000; i++) {
            sum += random.nextLong();
        }
    }

5.1 网页版本: 分析思路与操作步骤:

  1. 抓取 Trace:使用方法一(Perfetto)录制,在录制过程中疯狂滑动 RecyclerView。
  2. 打开并定位
    • 用 Chrome 打开 trace 文件。
    • 直接查看 Alerts 面板 ,很可能会有一条 Expensive measure/layout pass 的警告。点击它,视图会自动定位到问题帧。
  3. 聚焦 UI Thread
    • 放大后,观察 UI ThreaddoFrame
    • 发现 doFrame 耗时极长(比如 40ms)。
    • 点击展开 doFrame,你会发现耗时主要集中在一个名为 RecyclerView.onBindViewHolderRecyclerView.onLayout 的方法上。
  4. 深入分析
    • onBindViewHolder 耗时 :这意味着你在 onBindViewHolder 中进行了繁重操作,如解析数据、解码图片、设置复杂监听器等。
    • onLayout/measure 耗时 :这可能是因为你的 Item 布局层级太深,或者使用了性能较差的布局容器(如 RelativeLayout 嵌套过多)。
  5. 验证
    • 在代码中优化你的 onBindViewHolder 方法(避免主线程IO、简化逻辑)和 Item 布局(使用 ConstraintLayout 压平层级)。
    • 再次抓取 Trace 进行对比,你会发现 doFrame 的耗时显著缩短,帧率恢复绿色。

所以网页版本可以参考这个系列博客: zhuanlan.zhihu.com/p/713438834

5.2 Studio中的Profiler版本: 分析思路与操作步骤:

比起网页版本,简单容易懂多了,没有那么复杂! 也是2024年集成到Studio中的

google官网的地址:

developer.android.google.cn/studio/prof...

1.录制,选择 "Capture System Activities"

2. 找到 Frame Timeline 区域,查看红色高亮的"Janky"帧(即掉帧)。得到哪个类

Expected duration和Actual duration时间

3 点击某一帧,查看其详细耗时分布(如 input、measure/layout、draw、sync、GPU)。得到哪个方法

frameChart (帧时间线图)

这个版本是网页版本的阉割版本,比较精简,看不到主线程的,只能看到自己的应用

6. System Trace 总结

System Trace存在2个版本,网页版本和studio中的profiler版本

  • 它是什么:System Trace 是 Android 性能分析的"终极武器",提供了系统级的、带时间线的详细视图。
  • 核心价值 :它最大的优势在于能揭示线程之间的协作与等待关系,让你看清跨进程、跨线程的调用链,从而定位性能瓶颈的根因,而非只是表面现象。
  • 学习曲线 :初次接触会感到复杂,但一旦掌握了 "四步分析法"(定位坏帧 -> 沿流水线溯源 -> 检查资源 -> 利用Alerts),就能快速上手。
  • 最佳实践 :不要孤立地使用它。通常的流程是:Systrace/Perfetto 定位大致方向 -> Android Studio Profiler 的 Method Trace 定位到具体代码行 -> 代码优化 -> 再次抓 Trace 验证效果

掌握 System Trace,意味着你拥有了洞察系统内部运作的能力,任何性能问题在你面前都将无所遁形。

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

相关推荐
IT_陈寒35 分钟前
Python开发者必知的5个高效技巧,让你的代码速度提升50%!
前端·人工智能·后端
zm4351 小时前
浅记Monaco-editor 初体验
前端
超凌1 小时前
vue element-ui 对表格的单元格边框加粗
前端
前端搬运侠1 小时前
🚀 TypeScript 中的 10 个隐藏技巧,让你的代码更优雅!
前端·typescript
CodeTransfer1 小时前
css中animation与js的绑定原来还能这样玩。。。
前端·javascript
liming4951 小时前
运行node18报错
前端
20261 小时前
14.7 企业级脚手架-制品仓库发布使用
前端·vue.js
coding随想1 小时前
揭秘HTML5的隐藏开关:监控资源加载状态readyState属性全解析!
前端
coding随想1 小时前
等待页面加载事件用window.onload还是DOMContentLoaded,一文给你讲清楚
前端