android 流畅度优化笔记(一)

15 阅读1分钟

获取帧数和每帧耗时,一般有三种方式:

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)
           }
       }
   }
}