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等总时间,但无法看到具体嵌套层级和测量过程 能清晰看到measurelayoutdraw各个阶段的耗时和调用栈,直观展示嵌套深带来的性能瓶颈
适合场景 线上监控,快速发现和上报卡顿问题 线下深度分析,定位卡顿的根本原因和优化方向

trace文件和项目案例:

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

相关推荐
CodeSheep6 小时前
甲骨文严查Java授权,公司连夜删除JDK。。
前端·后端·程序员
前端fighter6 小时前
Async/Await 实现原理
前端·javascript·面试
MiniCode6 小时前
EllipsizeEndTextview末尾省略自定义View
android·java·前端
前端小巷子6 小时前
Vue3 模板编译优化
前端·vue.js·面试
不爱说话郭德纲6 小时前
🔥面试官:说说看,用户登录后拿到的 Token,你应该怎么存?存哪里?
前端·安全·面试
艾小码6 小时前
React 渲染流程深度解析(结合 react-reconciler)
前端·javascript·react.js
coding随想6 小时前
移动端H5手势事件(Gesture Events)全解析:只是触摸事件(Touch Events)的封装吗?
前端
玖伍贰零壹肆6 小时前
Promise的玩法
前端
WindrunnerMax6 小时前
在富文本编辑器中实现 Markdown 流式增量解析算法
前端·github·aigc