前面讲了卡顿的原理,原因,监控手段和分析手段,各种工具
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...