获取帧数和每帧耗时,一般有三种方式:
1。通过 choreographer 注册 FrameCallback,计算两帧间的时长
class FrameMonitor {
private var frameCount = 0
private var lastFrameTimeNanos = 0L
private var frameRate = 0
private val frameIntervalNanos = 16666666L // 60 FPS 对应的纳秒数
private var isMonitoring = false
// 帧回调
private val frameCallback = object : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
frameCount++
if (lastFrameTimeNanos != 0L) {
val frameTimeMs = (frameTimeNanos - lastFrameTimeNanos) / 1_000_000.0
// 处理每帧耗时
handleFrameTime(frameTimeMs)
}
lastFrameTimeNanos = frameTimeNanos
// 每秒计算一次帧率
if (frameCount % 60 == 0) {
val currentTime = System.currentTimeMillis()
// 这里可以计算实际帧率
// frameRate = frameCount / elapsedSeconds
}
if (isMonitoring) {
Choreographer.getInstance().postFrameCallback(this)
}
}
}
fun startMonitoring() {
isMonitoring = true
Choreographer.getInstance().postFrameCallback(frameCallback)
}
fun stopMonitoring() {
isMonitoring = false
Choreographer.getInstance().removeFrameCallback(frameCallback)
}
private fun handleFrameTime(frameTimeMs: Double) {
// 帧耗时超过 16.67ms(60FPS)可能有问题
if (frameTimeMs > 16.67) {
Log.d("FrameMonitor", "Frame drop detected: ${frameTimeMs}ms")
}
}
}
在 Jetpack Compose 中监控
@Composable
fun FrameTimeMonitor() {
val frameTimeNanos = remember { mutableStateOf(0L) }
DisposableEffect(Unit) {
val choreographer = Choreographer.getInstance()
val frameCallback = object : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
frameTimeNanos.value = frameTimeNanos
choreographer.postFrameCallback(this)
}
}
choreographer.postFrameCallback(frameCallback)
onDispose {
choreographer.removeFrameCallback(frameCallback)
}
}
// 使用 frameTimeNanos.value 计算帧耗时
}
屏幕刷新率
WindowManager windowManager = (WindowMananger) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisPlay();
float refreshRating = display.getRefreshRate();
Choreographer 是 ThreadLocal的,不同线程调用Chroegrapher.getInstance,其返回值不同
2.通过FrameMetrics 获取每帧耗时
@RequiresApi(Build.VERSION_CODES.N)
class FrameMetricsMonitor(private val activity: Activity) {
private var frameMetricsAvailableListener: Window.OnFrameMetricsAvailableListener? = null
fun start() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
frameMetricsAvailableListener =
Window.OnFrameMetricsAvailableListener { _, frameMetrics, _ ->
val totalDurationMs =
frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION) / 1_000_000.0
val drawDurationMs =
frameMetrics.getMetric(FrameMetrics.DRAW_DURATION) / 1_000_000.0
val layoutMeasureDurationMs =
frameMetrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION) / 1_000_000.0
Log.d("FrameMetrics",
"Total: ${"%.2f".format(totalDurationMs)}ms, " +
"Draw: ${"%.2f".format(drawDurationMs)}ms, " +
"Layout: ${"%.2f".format(layoutMeasureDurationMs)}ms")
}
activity.window.addOnFrameMetricsAvailableListener(
frameMetricsAvailableListener,
Handler(Looper.getMainLooper())
)
}
}
fun stop() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
frameMetricsAvailableListener?.let {
activity.window.removeOnFrameMetricsAvailableListener(it)
}
}
}
}