前面讲了卡顿的原理,原因,监控手段和分析手段,各种工具
Android <卡顿一> 深入理解Android 卡顿Choreographer:从VSYNC到掉帧(卡顿原理)
Android <卡顿二> 突破性性能监控方案Matrix---揭开微信亿级用户背后的流畅秘密 (卡顿监控工具集成)
Android <卡顿三> 卡顿性能分析工具 SystemTrace 精准定位 Android 性能瓶颈 (工具使用)
Android <卡顿四> 卡顿性能第二代工具 Perfetto 精准定位 Android 性能瓶颈 (工具使用)
1.过渡绘制动画导致卡顿案例
做360度的旋转后空翻的效果动画

ini
package com.evenbus.myapplication.trace;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import androidx.appcompat.app.AppCompatActivity;
import com.evenbus.myapplication.R;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 极端卡顿动画演示Activity - 演示大量视图和复杂变换导致的严重性能问题
* 注意:此代码专门设计为制造严重卡顿,用于性能测试目的
*/
public class LaggyAnimationActivity extends AppCompatActivity {
private View animationView;
private List<View> views = new ArrayList<>();
private ValueAnimator animator;
private Random random = new Random();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_laggy_animation);
setTitle("复杂变换导致的严重卡顿");
animationView = findViewById(R.id.animation_view);
ViewGroup container = findViewById(R.id.container);
// 创建大量视图 - 10000个视图
for (int i = 0; i < 10000; i++) {
View view = new View(this);
view.setLayoutParams(new ViewGroup.LayoutParams(20, 20));
// 使用随机颜色
int color = Color.argb(200,
random.nextInt(256),
random.nextInt(256),
random.nextInt(256));
view.setBackgroundColor(color);
container.addView(view);
views.add(view);
}
// 启动极端卡顿动画
startExtremeLaggyAnimation();
}
private void startExtremeLaggyAnimation() {
// 创建复杂的运动路径
Path path = new Path();
path.moveTo(0, 0);
for (int i = 1; i <= 50; i++) {
path.lineTo(i * 20, (float) (Math.sin(i * 0.2) * 200 + Math.cos(i * 0.1) * 100));
}
final PathMeasure pathMeasure = new PathMeasure(path, false);
final float pathLength = pathMeasure.getLength();
animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(4000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(animation -> {
updateExtremeAnimation(animation, pathLength, pathMeasure);
});
animator.start();
}
private void updateExtremeAnimation(ValueAnimator animation, float pathLength, PathMeasure pathMeasure) {
float fraction = (float) animation.getAnimatedValue();
float distance = fraction * pathLength;
float[] pos = new float[2];
pathMeasure.getPosTan(distance, pos, null);
// 更新主动画视图
animationView.setTranslationX(pos[0]);
animationView.setTranslationY(pos[1]);
// 极端复杂的视图变换 - 每帧更新10000个视图的多个属性
for (int i = 0; i < views.size(); i++) {
View view = views.get(i);
// 1. 复杂的波浪形移动
float waveOffsetX = (float) Math.sin(i * 0.01f + fraction * Math.PI * 4) * 60;
float waveOffsetY = (float) Math.cos(i * 0.008f + fraction * Math.PI * 3) * 40;
float circularX = (float) Math.cos(i * 0.005f + fraction * Math.PI * 2) * 50;
float circularY = (float) Math.sin(i * 0.005f + fraction * Math.PI * 2) * 30;
view.setTranslationX(pos[0] + i * 4 + waveOffsetX + circularX);
view.setTranslationY(pos[1] + i * 3 + waveOffsetY + circularY);
// 2. 复杂的多层旋转
float baseRotation = fraction * 360 * 6; // 基础快速旋转
float waveRotation = (float) Math.sin(i * 0.015f + fraction * Math.PI * 5) * 120; // 正弦波动
float indexRotation = i * 0.3f; // 基于索引的旋转
view.setRotation(baseRotation + waveRotation + indexRotation);
// 3. 复杂的脉冲缩放效果
float pulse = (float) (0.4f + Math.sin(fraction * Math.PI * 8 + i * 0.012f) * 0.3f);
float scaleVariationX = (float) Math.cos(i * 0.004f + fraction * Math.PI) * 0.15f;
float scaleVariationY = (float) Math.sin(i * 0.004f + fraction * Math.PI) * 0.15f;
view.setScaleX(pulse + scaleVariationX);
view.setScaleY(pulse + scaleVariationY);
// 4. 复杂的波浪式透明度变化
float alphaWave1 = (float) Math.sin(fraction * Math.PI * 3 + i * 0.01f) * 0.4f;
float alphaWave2 = (float) Math.cos(fraction * Math.PI * 2 + i * 0.008f) * 0.3f;
float baseAlpha = 0.3f;
float alpha = baseAlpha + alphaWave1 + alphaWave2;
view.setAlpha(Math.max(0.1f, Math.min(1.0f, alpha)));
// 5. 动态颜色变化(每50个视图更新一次)
if (i % 50 == 0) {
int red = (int) ((Math.sin(fraction * Math.PI * 2 + i * 0.001f) * 0.5f + 0.5f) * 255);
int green = (int) ((Math.cos(fraction * Math.PI * 3 + i * 0.002f) * 0.5f + 0.5f) * 255);
int blue = (int) ((Math.sin(fraction * Math.PI * 4 + i * 0.003f) * 0.5f + 0.5f) * 255);
int color = Color.argb(200, red, green, blue);
view.setBackgroundColor(color);
}
}
// 每帧执行重型计算来加剧卡顿
performHeavyFrameCalculations();
}
/**
* 每帧执行的重型数学计算 - 阻塞UI线程
*/
private void performHeavyFrameCalculations() {
double result = 0;
// 复杂的数学计算
for (int j = 0; j < 15000; j++) {
// 多种三角函数和数学运算组合
double angle = j * 0.1;
result += Math.sin(angle) * Math.cos(angle * 2)
* Math.tan(angle * 0.5 + 0.1)
* Math.log(j + 2);
// 添加条件判断增加计算复杂度
if (j % 500 == 0) {
result *= Math.sqrt(j + 1);
}
}
// 创建临时对象触发GC
if (System.currentTimeMillis() % 1000 < 16) { // 大约每帧一次
List<String> tempObjects = new ArrayList<>();
for (int k = 0; k < 50; k++) {
tempObjects.add("calculation_temp_" + k + "_" + result);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (animator != null) {
animator.cancel();
}
if (views != null) {
views.clear();
}
}
/**
* 获取当前视图数量
*/
public int getViewCount() {
return views != null ? views.size() : 0;
}
/**
* 停止动画
*/
public void stopAnimation() {
if (animator != null) {
animator.cancel();
}
}
}
动画计算复杂度爆炸
arduino
for (int i = 0; i < views.size(); i++) { // 10,000次循环
// 每个视图需要计算:
// - 4个三角函数计算(移动)
// - 3个三角函数计算(旋转)
// - 4个三角函数计算(缩放)
// - 3个三角函数计算(透明度)
// - 条件性的颜色计算
}
每帧总共需要执行约 100,000+ 次三角函数计算,这是极其昂贵的操作。
2.用Matrix分析上面案例
2.1 log日志

2.2 json报告
json
{
"machine": "BEST", // 设备性能等级:BEST表示高性能设备
"cpu_app": 0, // 应用CPU使用率:0%表示CPU使用数据未正确采集或应用未占用CPU
"mem": 11901149184, // 设备总内存:11.9GB,高端设备配置
"mem_free": 4352848,// 空闲内存:4.25MB,内存使用接近饱和,压力极大
"scene": "com.evenbus.myapplication.trace.LaggyAnimationActivity", // 当前活动场景
"dropLevel": { // 丢帧等级分布
"DROPPED_BEST": 0, // 最佳等级丢帧数:0
"DROPPED_NORMAL": 0, // 正常等级丢帧数:0
"DROPPED_MIDDLE": 113, // 中等等级丢帧数:113次 - 严重丢帧
"DROPPED_HIGH": 0, // 高等级丢帧数:0
"DROPPED_FROZEN": 0 // 冻结等级丢帧数:0
},
"dropSum": { // 累计丢帧总数
"DROPPED_BEST": 0, // 最佳等级累计丢帧:0
"DROPPED_NORMAL": 0, // 正常等级累计丢帧:0
"DROPPED_MIDDLE": 1089, // 中等等级累计丢帧:1089帧 - 极端严重
"DROPPED_HIGH": 0, // 高等级累计丢帧:0
"DROPPED_FROZEN": 0 // 冻结等级累计丢帧:0
},
"fps": 5.60915470123291, // 帧率:5.6 FPS,严重卡顿(正常应为60FPS)
"UNKNOWN_DELAY_DURATION": 81198792, // 未知延迟时长:81.20ms(主要是GC垃圾回收)
"INPUT_HANDLING_DURATION": 3001, // 输入处理时长:3ms(正常)
"ANIMATION_DURATION": 67112243, // 动画计算时长:67.11ms - 主要瓶颈
"LAYOUT_MEASURE_DURATION": 146379, // 布局测量时长:0.15ms(正常)
"DRAW_DURATION": 2576376, // 绘制时长:2.58ms(正常)
"SYNC_DURATION": 16843537, // 同步时长:16.84ms(较高,属性同步开销)
"COMMAND_ISSUE_DURATION": 8579170, // 命令提交时长:8.58ms(较高)
"SWAP_BUFFERS_DURATION": 344337, // 缓冲区交换时长:0.34ms(正常)
"TOTAL_DURATION": 178279885, // 总帧时长:178.28ms(严重超标,正常应16.67ms)
"GPU_DURATION": 1327788, // GPU处理时长:1.33ms(正常)
"DROP_COUNT": 10, // 丢帧事件次数:10次
"REFRESH_RATE": 60, // 屏幕刷新率:60Hz
"tag": "Trace_FPS", // 数据标签:帧率追踪
"process": "com.evenbus.myapplication", // 进程名称
"time": 1756525459898 // 时间戳:2025年1月1日左右
}
2.3 极端严重卡顿 - FPS仅5.6
根本原因分析:
-
ANIMATION_DURATION: 67.11ms (37.7%) - 动画计算严重超时
- 在UI线程执行重型数学计算
- 10000个视图的复杂变换计算
-
UNKNOWN_DELAY: 81.20ms (45.6%) - 垃圾回收阻塞
- 频繁创建临时对象触发GC
- 内存压力极大(空闲仅4.25MB)
-
SYNC_DURATION: 16.84ms (9.5%) - 属性同步开销
- 每帧40000+次属性设置
性能指标解读:
指标 | 实际值 | 正常值 | 超标倍数 | 问题严重度 |
---|---|---|---|---|
FPS | 5.6 | 60 | 10.7倍 | 🔴 极端严重 |
总帧时长 | 178ms | 16.7ms | 10.7倍 | 🔴 极端严重 |
丢帧数 | 1089帧 | 0帧 | - | 🔴 极端严重 |
空闲内存 | 4.25MB | >100MB | 严重不足 | 🔴 极端严重 |
2.4 动画导致的卡顿
关键证据:ANIMATION_DURATION异常高
json
"ANIMATION_DURATION":67112243 // 67.11ms - 这是决定性证据!
1. ANIMATION_DURATION的含义
- 这个指标专门表示动画计算和更新的耗时
- 包括:属性动画、视图变换、数学计算等
- 正常值应该小于5ms

3.用Systrace分析上面案例
3.1 看帧:Jank frames

3.2 主线程:提示是动画执行中

3.3 火焰图:

4.用Pefetto分析上面案例
4.1 卡顿的原因

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

4.3 主线程

4.4 火焰图
cpu调度:

5. 总结
Matrix 监控、Systrace/Perfetto 分析动画卡顿的特点
针对动画卡顿的分析对比
5.1 Matrix的表现:
json
// 量化指标:
"ANIMATION_DURATION":67112243 // 动画计算耗时67.11ms
"fps":5.6 // 帧率极低
"DROP_COUNT":10 // 10次丢帧事件
// 但无法显示:
- 具体是哪个动画导致问题
- 动画的属性变化过程
- 渲染管道的具体瓶颈
5.2 Systrace/Perfetto的表现:
diff
// 可视化分析:
- 显示Choreographer的VSync信号
- 显示UI线程的onAnimationUpdate调用
- 显示RenderThread的绘制命令
// 帧分析:
- 可以查看每一帧的动画计算时间
- 可以看到属性更新的具体耗时
- 显示GPU渲染动画帧的时间
trace文件和项目案例:
RecyclerView卡顿案例地址: github.com/pengcaihua1...