8. Android <卡顿八>破局Android卡顿困局!基于Matrix+Systrace/Perfetto的布局嵌套深度优化实战(卡顿实战)

前面讲了卡顿的原理,原因,监控手段和分析手段,各种工具

Android <卡顿一> 深入理解Android 卡顿Choreographer:从VSYNC到掉帧(卡顿原理)

Android <卡顿二> 突破性性能监控方案Matrix---揭开微信亿级用户背后的流畅秘密 (卡顿监控工具集成)

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

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

1.布局嵌套深导致卡顿案例

添加100个视图嵌套,叠加!

scss 复制代码
package com.evenbus.myapplication.trace;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.evenbus.myapplication.R;

import java.util.Random;

/**
 * 布局性能追踪Activity - 演示复杂布局导致的性能问题
 * 该Activity展示嵌套布局和动态添加视图对性能的影响
 */
public class TraceLayoutActivity extends AppCompatActivity {

    // 视图组件引用
    private LinearLayout containerLayout;    // 主容器布局
    private View nestedLayout;               // 嵌套布局视图
    private View flatLayout;                 // 扁平布局视图
    private TextView tvPerformance;          // 性能状态显示
    private TextView tvViewCount;            // 视图数量显示
    private Button btnShowNested;            // 显示嵌套布局按钮
    private Button btnShowFlat;              // 显示扁平布局按钮
    private Button btnAddDynamicViews;       // 添加动态视图按钮
    private Button btnClearViews;            // 清除视图按钮

    // 状态和工具变量
    private int viewCount = 0;               // 当前视图数量计数
    private Random random = new Random();    // 随机数生成器
    private Handler handler = new Handler(Looper.getMainLooper()); // 主线程Handler

    /**
     * Activity创建生命周期回调
     * @param savedInstanceState 保存的实例状态
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setTitle("嵌套层级太大卡顿");
        // 设置布局文件
        setContentView(R.layout.activity_trace_layout_layout);

        // 初始化视图组件
        initViews();

        // 设置按钮点击监听器
        setupClickListeners();
    }

    /**
     * 初始化所有视图组件
     */
    private void initViews() {
        containerLayout = findViewById(R.id.container_layout);
        nestedLayout = findViewById(R.id.nested_layout);
        flatLayout = findViewById(R.id.flat_layout);
        tvPerformance = findViewById(R.id.tv_performance);
        tvViewCount = findViewById(R.id.tv_view_count);
        btnShowNested = findViewById(R.id.btn_show_nested);
        btnShowFlat = findViewById(R.id.btn_show_flat);
        btnAddDynamicViews = findViewById(R.id.btn_add_dynamic_views);
        btnClearViews = findViewById(R.id.btn_clear_views);
    }

    /**
     * 设置按钮点击事件监听器
     */
    private void setupClickListeners() {
        // 显示嵌套布局按钮点击事件
        btnShowNested.setOnClickListener(v -> showNestedLayout());

        // 显示扁平布局按钮点击事件
        btnShowFlat.setOnClickListener(v -> showFlatLayout());

        // 添加动态视图按钮点击事件
        btnAddDynamicViews.setOnClickListener(v -> addDynamicViews());

        // 清除视图按钮点击事件
        btnClearViews.setOnClickListener(v -> clearAllViews());
    }

    /**
     * 显示嵌套布局 - 演示复杂布局导致的性能问题
     */
    private void showNestedLayout() {
        // 先清除现有视图
        clearAllViews();

        // 模拟测量和布局的耗时操作
        simulateHeavyLayoutWork();

        // 将嵌套布局添加到容器中
        nestedLayout.setVisibility(View.VISIBLE);
        ViewGroup parent = (ViewGroup) nestedLayout.getParent();

        // 如果嵌套布局已有父布局,先从其父布局中移除
        if (parent != null) {
            parent.removeView(nestedLayout);
        }

        // 添加到主容器
        containerLayout.addView(nestedLayout);

        // 更新视图数量显示(估计嵌套布局中的视图数量)
        updateViewCount(50);

        // 更新性能状态提示
        updatePerformanceStatus("显示嵌套布局 - 可能会卡顿");
    }

    /**
     * 显示扁平布局 - 演示优化后的布局性能
     */
    private void showFlatLayout() {
        // 先清除现有视图
        clearAllViews();

        // 将扁平布局添加到容器中
        flatLayout.setVisibility(View.VISIBLE);
        ViewGroup parent = (ViewGroup) flatLayout.getParent();

        // 如果扁平布局已有父布局,先从其父布局中移除
        if (parent != null) {
            parent.removeView(flatLayout);
        }

        // 添加到主容器
        containerLayout.addView(flatLayout);

        // 更新视图数量显示(估计扁平布局中的视图数量)
        updateViewCount(25);

        // 更新性能状态提示
        updatePerformanceStatus("显示扁平布局 - 流畅");
    }

    /**
     * 动态添加大量视图 - 演示主线程阻塞导致的卡顿
     */
    private void addDynamicViews() {
        // 更新性能状态提示
        updatePerformanceStatus("正在添加视图,主线程阻塞中...");

        // 在主线程执行耗时操作,故意造成卡顿
        // 使用Handler.postDelayed模拟异步操作,但实际上仍在主线程执行
        handler.postDelayed(() -> {
            // 添加100个复杂视图
            for (int i = 0; i < 800; i++) {
                createAndAddComplexView();
            }

            // 更新性能状态提示
            updatePerformanceStatus("已添加100个复杂视图 - 严重卡顿风险");
        }, 100); // 延迟100ms执行,避免立即阻塞UI
    }

    /**
     * 创建并添加复杂嵌套视图 - 演示视图创建和添加的性能开销
     */
    private void createAndAddComplexView() {
        // 创建一个复杂的嵌套视图结构

        // 外层横向布局
        LinearLayout outerLayout = new LinearLayout(this);
        outerLayout.setOrientation(LinearLayout.HORIZONTAL);
        outerLayout.setBackgroundColor(0x33FF0000); // 半透明红色背景
        LinearLayout.LayoutParams outerParams = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        outerParams.setMargins(4, 4, 4, 4);
        outerLayout.setLayoutParams(outerParams);

        // 左侧嵌套纵向布局
        LinearLayout leftLayout = new LinearLayout(this);
        leftLayout.setOrientation(LinearLayout.VERTICAL);
        leftLayout.setBackgroundColor(0x3300FF00); // 半透明绿色背景
        LinearLayout.LayoutParams leftParams = new LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1); // 权重为1
        leftParams.setMargins(2, 2, 2, 2);
        leftLayout.setLayoutParams(leftParams);

        // 在左侧布局中添加3个TextView
        for (int j = 0; j < 3; j++) {
            TextView textView = new TextView(this);
            textView.setText("Item " + viewCount + "-" + j);
            textView.setBackgroundColor(0x330000FF); // 半透明蓝色背景
            textView.setPadding(8, 8, 8, 8);
            leftLayout.addView(textView);
            viewCount++; // 增加视图计数
        }

        // 右侧嵌套纵向布局
        LinearLayout rightLayout = new LinearLayout(this);
        rightLayout.setOrientation(LinearLayout.VERTICAL);
        rightLayout.setBackgroundColor(0x33FFFF00); // 半透明黄色背景
        LinearLayout.LayoutParams rightParams = new LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1); // 权重为1
        rightParams.setMargins(2, 2, 2, 2);
        rightLayout.setLayoutParams(rightParams);

        // 在右侧布局中添加2个Button
        for (int j = 0; j < 2; j++) {
            Button button = new Button(this);
            button.setText("Btn " + viewCount + "-" + j);
            button.setPadding(8, 8, 8, 8);
            rightLayout.addView(button);
            viewCount++; // 增加视图计数
        }

        // 将左右布局添加到外层布局
        outerLayout.addView(leftLayout);
        outerLayout.addView(rightLayout);

        // 将外层布局添加到主容器
        containerLayout.addView(outerLayout);

        // 计数外层布局(1个外层 + 2个内层 = 3个布局视图)
        viewCount += 3;

        // 更新视图数量显示
        updateViewCount(viewCount);
    }

    /**
     * 清除所有动态添加的视图
     */
    private void clearAllViews() {
        // 移除容器中的所有子视图
        containerLayout.removeAllViews();

        // 重置视图计数器
        viewCount = 0;

        // 更新视图数量显示
        updateViewCount(viewCount);

        // 更新性能状态提示
        updatePerformanceStatus("已清除所有视图");
    }

    /**
     * 更新视图数量显示
     * @param count 当前视图数量
     */
    private void updateViewCount(int count) {
        tvViewCount.setText("视图数量: " + count);
    }

    /**
     * 更新性能状态显示
     * @param status 性能状态文本
     */
    private void updatePerformanceStatus(String status) {
        tvPerformance.setText("性能状态: " + status);
    }

    /**
     * 模拟繁重的布局计算工作 - 演示测量和布局过程的性能开销
     */
    private void simulateHeavyLayoutWork() {
//        // 记录开始时间
//        long startTime = System.currentTimeMillis();
//
//        // 执行复杂的数学计算来模拟布局计算耗时
//        double result = 0;
//        for (int i = 0; i < 1000000; i++) {
//            result += Math.sin(i) * Math.cos(i);
//        }
//
//        // 计算耗时
//        long duration = System.currentTimeMillis() - startTime;

        // 更新性能状态显示
        updatePerformanceStatus("布局计算耗时: " + 200 + "ms");
    }

    /**
     * Activity销毁生命周期回调
     * 进行资源清理工作
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 移除所有Handler回调,避免内存泄漏
        handler.removeCallbacksAndMessages(null);
    }
}

大量动态视图创建

scss 复制代码
// 一次创建800个复杂嵌套视图!
for (int i = 0; i < 800; i++) {
    createAndAddComplexView(); // 每个视图包含6个子视图
}
  • 问题 :创建800 × 6 = 4800个视图
  • 影响:布局计算开销

2.用Matrix分析上面案例

2.1 log日志

会有2个报告,慢方法和FPS

慢方法

FPS

2.2 json报告

json 复制代码
{
    "machine": "BEST",  // 设备性能等级:BEST表示高性能设备
    "cpu_app": 0,       // 应用CPU使用率:0%表示CPU使用数据未正确采集或应用未占用CPU
    "mem": 11901149184, // 设备总内存:11.9GB,高端设备配置
    "mem_free": 4099168,// 空闲内存:4.0MB,内存使用接近饱和,有一定压力
    "detail": "NORMAL", // 性能评级:NORMAL表示正常级别,不是严重问题
    "cost": 1170,       // 总方法执行耗时:1170毫秒(1.17秒)
    "scene": "com.evenbus.myapplication.trace.TraceLayoutActivity",  // 监控场景:布局追踪Activity
    "stack": "0,1048574,1,1170\n1,192,1,1164\n2,181,1,15\n2,181,1,11\n2,181,1,5\n2,181,1,5\n2,181,1,5\n2,181,1,6\n2,181,1,5\n2,181,1,5\n2,181,1,6\n2,181,1,5\n2,181,1,5\n2,181,1,5\n2,181,1,6\n2,181,1,5\n2,181,1,6\n2,181,1,5\n2,181,1,5\n2,181,1,7\n2,181,1,5\n2,181,1,6\n2,181,1,6\n2,181,1,5\n2,181,1,6\n2,181,1,5\n2,181,1,6\n2,181,1,5\n2,181,1,6\n2,181,1,5\n",  // 方法调用栈详情
    "stackKey": "192|", // 关键方法标识:方法ID 192是主要性能热点
    "tag": "Trace_EvilMethod",  // 追踪类型:邪恶方法追踪(耗时方法分析)
    "process": "com.evenbus.myapplication",  // 进程名称
    "time": 1756532915669  // 时间戳:2025年1月1日左右
}

2.3 性能瓶颈明确

arduino 复制代码
"1,192,1,1164"  // 这个方法是主要性能热点!
  • 耗时1164ms,占总时间的99.5%
  • 这是需要重点优化的方法

找到对应的方法:createAndAddComplexView

2.4 FPS的报告

json 复制代码
{
    "machine": "BEST",  // 设备性能等级:BEST表示高端设备,排除硬件性能不足问题
    "cpu_app": 0,       // 应用CPU使用率:0%表示CPU使用数据未正确采集或应用未大量占用CPU
    "mem": 11901149184, // 设备总内存:11.9GB,充足的内存资源
    "mem_free": 4005352,// 空闲内存:4.0MB,内存使用接近饱和,存在内存压力
    "scene": "com.evenbus.myapplication.trace.TraceLayoutActivity",  // 监控场景:布局性能测试Activity
    "dropLevel": {      // 丢帧等级分布 - 显示轻微丢帧
        "DROPPED_BEST": 224,    // 最佳等级丢帧:224次(轻微丢帧事件)
        "DROPPED_NORMAL": 1,    // 正常等级丢帧:1次
        "DROPPED_MIDDLE": 0,    // 中等丢帧:0次
        "DROPPED_HIGH": 0,      // 高等级丢帧:0次
        "DROPPED_FROZEN": 0     // 冻结丢帧:0次(无严重卡顿)
    },
    "dropSum": {        // 累计丢帧总数 - 丢帧数量很少
        "DROPPED_BEST": 2,      // 最佳等级累计丢帧:2帧
        "DROPPED_NORMAL": 3,    // 正常等级累计丢帧:3帧
        "DROPPED_MIDDLE": 0,    // 中等累计丢帧:0帧
        "DROPPED_HIGH": 0,      // 高等级累计丢帧:0帧
        "DROPPED_FROZEN": 0     // 冻结累计丢帧:0帧
    },
    "fps": 58.72233200073242,   // 帧率:58.7 FPS - 低于60Hz标准,存在性能问题
    "UNKNOWN_DELAY_DURATION": 439502,  // 未知延迟时长:0.44ms(主要是GC垃圾回收等待)
    "INPUT_HANDLING_DURATION": 19599,   // 输入处理时长:0.02ms(正常,无输入阻塞)
    "ANIMATION_DURATION": 87464,        // 动画计算时长:0.09ms(正常,动画计算不是瓶颈)
    "LAYOUT_MEASURE_DURATION": 94576,   // 布局测量时长:0.09ms(正常,布局复杂度可控)
    "DRAW_DURATION": 3948203,           // 绘制时长:3.95ms - 🚨严重问题!占帧时间23.7%
    "SYNC_DURATION": 385608,            // 同步时长:0.39ms(正常)
    "COMMAND_ISSUE_DURATION": 3368769,  // 命令提交时长:3.37ms - 🚨严重问题!占帧时间20.2%
    "SWAP_BUFFERS_DURATION": 896481,    // 缓冲区交换时长:0.90ms(正常)
    "TOTAL_DURATION": 11181776,         // 总帧时长:11.18ms(理论上应达89FPS,但实际只有58.7FPS)
    "GPU_DURATION": 2797737,            // GPU处理时长:2.80ms - ⚠️偏高,占帧时间16.8%
    "DROP_COUNT": 0,                    // 丢帧事件次数:0次(无严重丢帧事件)
    "REFRESH_RATE": 60,                 // 屏幕刷新率:60Hz
    "tag": "Trace_FPS",                 // 数据标签:帧率性能追踪
    "process": "com.evenbus.myapplication",  // 进程名称
    "time": 1756533687685               // 时间戳:2025年1月1日左右
}

这个是因为滚动了,导致曾经高,滑动不流畅

决定性证据分析:

2.4.1. DRAW_DURATION异常高 (3.95ms)

json 复制代码
"DRAW_DURATION":3948203  // 3.95ms,严重超标!

原因:过度绘制(Overdraw)

  • 多个视图重叠且都设置背景
  • 自定义View中进行复杂绘制
  • 没有使用canvas.clipRect()限制绘制区域

2.4.2. COMMAND_ISSUE_DURATION异常高 (3.37ms)

json 复制代码
"COMMAND_ISSUE_DURATION":3368769  // 3.37ms,严重超标!

原因:频繁的invalidate()调用

  • 在动画循环中频繁调用invalidate()
  • 没有使用正确的动画API(如ValueAnimator)
  • 在onDraw()中调用invalidate()形成死循环

2.4.3. GPU_DURATION偏高 (2.80ms)

json 复制代码
"GPU_DURATION":2797737  // 2.80ms,偏高

**原因**:GPU处理过多重叠绘制

  • 多个半透明视图重叠
  • 复杂的图层混合操作

3.用Systrace分析上面案例

3.1 看工具:核心指标, janky frames

3.2 看主线程: 指向的是draw,没有具体指向measure

3.3 看火焰图和Top down,具体的堆栈信息

4.用Pefetto分析上面案例

4.1 卡顿的原因

4.2 卡顿时间:看绘制的时间和实际花费的时间: Expected Timeline 和 Actual Timeline

前面部分正确,后面全红了

4.3 主线程

4.4 火焰图

5. 总结

Matrix 监控、Systrace/Perfetto 分析布局嵌套的特点

针对布局嵌套的分析对比

5.1 Matrix的表现:

json 复制代码
// 量化指标:
"LAYOUT_MEASURE_DURATION":94576        // 布局测量耗时
"DRAW_DURATION":3948203               // 绘制耗时
"SYNC_DURATION":385608                // 同步耗时

// 但无法显示:
- 具体的视图嵌套层级
- 过度绘制的可视化效果
- 布局过程的详细时间线

5.2 Systrace/Perfetto的表现:

diff 复制代码
// 可视化分析:
- 显示视图树和嵌套层级
- 显示measure/layout/draw的各阶段耗时
- 显示Overdraw区域和程度

// 帧分析:
- 可以查看每一帧的布局计算时间
- 可以看到视图测量的具体耗时
- 显示GPU渲染布局的时间

5.3 对比布局嵌套,3种工具的分析

分析维度 Matrix Systrace / Perfetto
定位方式 代码级定位 系统级定位
核心优势 精准定位到耗时方法 (createAndAddComplexView) 和 量化FPS/耗时 可视化整个渲染流水线 ,看到 VSYNC、UI线程、RenderThread 的协作和阻塞点
数据呈现 数值报告(JSON)、日志(EvilMethod栈信息) 图形化时间线(火焰图、CPU调度、系统事件)
分析层级 应用层:关注自身代码执行耗时 系统层:关注整个系统的调度、渲染、GPU工作负载
布局分析 能报告LAYOUT_MEASURE_DURATION等总时间,但无法看到具体嵌套层级和测量过程 能清晰看到measure、layout、draw各个阶段的耗时和调用栈,直观展示嵌套深带来的性能瓶颈
适合场景 线上监控,快速发现和上报卡顿问题 线下深度分析,定位卡顿的根本原因和优化方向

trace文件和项目案例:

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

相关推荐
RobinDevNotes38 分钟前
从 Electron 到 GPUI,9 个跨平台桌面方案该怎么选
前端·javascript·rust·electron·openresty·前端开发工具
jay神2 小时前
基于 YOLOv8 的轴承缺陷检测(Web 版)
前端·人工智能·yolo
jay神2 小时前
基于 YOLOv8 的森林火灾检测系统(Web 版)
前端·yolo
code2cat2 小时前
HTML基础篇之第一张网页:从index.html到浏览器里的任务清单
前端·html
miss3 小时前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan3 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真3 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment3 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan3 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么4 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模