概述
对于 Android 的耗时统计,在 Android 7.0, 增加了一个 Api 可以方便的统计一个 Window 的 View 树的绘制耗时。低于 API 26 可以使用 Choreographer
监听帧时间。
OnFrameMetricsAvailableListener 介绍
OnFrameMetricsAvailableListener
是 Android 提供的一个监听器接口,用于监测应用 UI 帧的性能指标,可以帮助开发者分析应用的流畅性和性能问题。
1. 作用
- 监听
FrameMetrics
数据,分析 每一帧 的性能信息。 - 获取 渲染时间、绘制时间、输入延迟 等关键指标。
- 帮助优化 掉帧、卡顿(Jank) 问题。
2. 适用场景
- 监测 UI 性能 ,发现 卡顿(Jank) 。
- 优化流畅度 ,减少 丢帧 和 长帧(> 16ms)。
- 调试复杂 UI 组件 ,分析 动画流畅性。
3. 基本用法
3.1 注册监听器
要使用 OnFrameMetricsAvailableListener
,可以通过 Window
注册监听:
java
import android.app.Activity;
import android.view.FrameMetrics;
import android.view.Window;
import android.view.Window.OnFrameMetricsAvailableListener;
import android.os.Handler;
import android.os.Looper;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取 Window 对象
Window window = getWindow();
// 注册监听器
window.addOnFrameMetricsAvailableListener(new OnFrameMetricsAvailableListener() {
@Override
public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
// 获取 Vsync 信号到帧完成的总时间(纳秒)
long totalDuration = frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION);
long totalMs = totalDuration / 1_000_000; // 转换为毫秒
// 输出帧渲染时间
System.out.println("Frame Time: " + totalMs + " ms");
}
}, new Handler(Looper.getMainLooper())); // 在主线程执行
}
}
4. 关键指标
FrameMetrics
提供了多个关键帧性能指标,使用 getMetric(int id)
获取:
指标名称 | 说明 |
---|---|
FrameMetrics.TOTAL_DURATION |
整帧渲染时间,从 Vsync 到帧完成的时间 |
FrameMetrics.INPUT_HANDLING_DURATION |
输入事件处理时间(用户触摸后到开始处理时间) |
FrameMetrics.ANIMATION_DURATION |
动画执行时间 |
FrameMetrics.LAYOUT_MEASURE_DURATION |
测量 & 布局时间 |
FrameMetrics.DRAW_DURATION |
绘制时间 |
FrameMetrics.SYNC_DURATION |
同步事务时间(View 刷新所需的 GPU 处理时间) |
FrameMetrics.COMMAND_ISSUE_DURATION |
CPU 端提交时间(将命令发送到 GPU 的时间) |
FrameMetrics.SWAP_BUFFERS_DURATION |
Swap Buffers 时间(显示到屏幕所需时间) |
FrameMetrics.GPU_COMPLETION_DURATION |
GPU 渲染时间 |
FrameMetrics.UNKNOWN_DELAY_DURATION |
未知延迟时间(其他未知影响) |
5. 计算掉帧情况
如果 TOTAL_DURATION
超过 16ms (60FPS),说明该帧 丢帧,可以计算掉帧数:
ini
java
复制编辑
int frameTimeMs = (int) (frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION) / 1_000_000);
int droppedFrames = frameTimeMs / 16; // 超过 16ms 计算掉帧数
if (droppedFrames > 0) {
System.out.println("Dropped Frames: " + droppedFrames);
}
6. 移除监听器
如果不再需要监听,可以移除:
ini
java
复制编辑
window.removeOnFrameMetricsAvailableListener(listener);
7. 适用 Android 版本
OnFrameMetricsAvailableListener
需要 API 26(Android 8.0)及以上。- 低于 API 26 可以使用
Choreographer
监听帧时间。
实践优化
- 如果
INPUT_HANDLING_DURATION
高,说明 输入事件处理 慢,优化onTouchEvent()
和dispatchTouchEvent()
。 - 如果
LAYOUT_MEASURE_DURATION
高,优化View.measure()
和View.layout()
,减少不必要的requestLayout()
。 - 如果
DRAW_DURATION
高,减少onDraw()
复杂度,使用ViewStub
或RecyclerView
复用视图。 - 如果
GPU_COMPLETION_DURATION
高,优化 GPU 负担,如减少Overdraw
和阴影效果
。
Choreographer 低版本介绍
Choreographer
是 Android 提供的 帧回调机制 ,用于 监听 UI 线程的绘制周期 ,可以在低版本(API 16+)上实现 帧率监测 和 性能分析。
1. 作用
- 监测 UI 渲染 :提供 VSync 信号 触发的 回调 ,用于监听 每一帧的绘制情况。
- 计算丢帧情况 :可以检测 帧率(FPS) ,发现 卡顿(Jank) 。
- 优化 UI 性能 :可用于 定时动画 、减少不必要的重绘。
2. 适用场景
- Android 4.1(API 16)及以上 设备 (比
OnFrameMetricsAvailableListener
兼容性更好) - 性能监测:统计帧率、掉帧
- 动画优化:精确控制动画帧同步
3. 使用示例
3.1 监听帧回调
使用 Choreographer
监听 每一帧的绘制周期:
java
import android.os.Handler;
import android.os.Looper;
import android.view.Choreographer;
public class FPSMonitor implements Choreographer.FrameCallback {
private long lastFrameTimeNanos = 0;
private int frameCount = 0;
public void start() {
Choreographer.getInstance().postFrameCallback(this); // 开始监听
}
@Override
public void doFrame(long frameTimeNanos) {
if (lastFrameTimeNanos > 0) {
// 计算当前帧间隔(单位:毫秒)
long frameIntervalMs = (frameTimeNanos - lastFrameTimeNanos) / 1_000_000;
System.out.println("Frame Time: " + frameIntervalMs + " ms");
// 检查掉帧情况
if (frameIntervalMs > 16) { // 16ms 代表 60FPS,超过表示丢帧
int droppedFrames = (int) (frameIntervalMs / 16);
System.out.println("Dropped Frames: " + droppedFrames);
}
}
lastFrameTimeNanos = frameTimeNanos;
frameCount++;
// 继续监听下一帧
Choreographer.getInstance().postFrameCallback(this);
}
public void stop() {
Choreographer.getInstance().removeFrameCallback(this); // 停止监听
}
}
3.2 在 Activity 里使用
typescript
FPSMonitor fpsMonitor;
@Override
protected void onResume() {
super.onResume();
fpsMonitor = new FPSMonitor();
fpsMonitor.start(); // 开启 FPS 监测
}
@Override
protected void onPause() {
super.onPause();
fpsMonitor.stop(); // 停止 FPS 监测
}
4. 计算 FPS(帧率)
在 Choreographer
回调中,每秒计算一次 FPS:
java
private int frameCount = 0;
private long startTime = System.currentTimeMillis();
@Override
public void doFrame(long frameTimeNanos) {
frameCount++;
long currentTime = System.currentTimeMillis();
if (currentTime - startTime >= 1000) { // 每秒计算一次 FPS
int fps = frameCount;
System.out.println("FPS: " + fps);
frameCount = 0;
startTime = currentTime;
}
// 继续监听下一帧
Choreographer.getInstance().postFrameCallback(this);
}
如果 FPS 低于 60,说明可能存在掉帧现象。
5. Choreographer
VS Handler.postDelayed()
对比项 | Choreographer | Handler.postDelayed() |
---|---|---|
适用 API 版本 | API 16+ | API 1+ |
帧同步机制 | VSync 信号(与 UI 线程同步) | 仅基于时间间隔(不一定与帧同步) |
适用于 | 性能监测、动画同步 | 定时任务(与 UI 刷新无关) |
如果需要 精准控制 UI 刷新(如帧率监测) ,Choreographer
比 Handler.postDelayed()
更精准。
6. 实践优化
- 如果
frameIntervalMs > 16ms
,说明 UI 丢帧 - 减少
onDraw()
中的复杂计算 - 避免 UI 线程的
阻塞任务
(如I/O
操作) - 使用
RecyclerView
代替ListView
进行高效列表滚动 - 优化
动画
,减少不必要的invalidate()
7. 适用 Android 版本
✅ Choreographer
适用于 Android 4.1+(API 16+)
✅ 比 OnFrameMetricsAvailableListener
兼容性更好
✅ 适用于 FPS 监测、掉帧分析、动画优化
如果你的项目支持 Android 8.0+(API 26+) ,可以使用 OnFrameMetricsAvailableListener
,否则 Choreographer
是更好的选择!
总结
✅ OnFrameMetricsAvailableListener
是监控 Android UI 性能的工具
✅ 能 获取每一帧的详细信息 ,帮助开发者发现卡顿问题
✅ 适用于 UI 调优 ,优化 丢帧、动画流畅度
✅ 适用于 Android 8.0+(API 26) ,低版本可以使用 Choreographer