Android性能优化:UI卡顿、帧率检测&优化

前言

本文主要分享:handler耗时检测、帧率、丢帧统计的方法。


handler耗时。帧率、丢帧统计.png

统计方法与实现

我们知道,Handler有个成员变量mLooper,它通过loop()方法取出需要执行的Message,message非空时,会根据是否有printer来打印开始、结束的log,我们可以自定义一个Printer,根据回调统计耗时。


handler原理.png

具体实现:

class HandlerLogger : Printer {
        companion object {
            private const val START = ">>>>> Dispatching to"
            private const val END = "<<<<< Finished to"
        }

        //打印log,可以统计时长,超过xxxms认为卡顿
        private var start = 0L

        override fun println(x: String?) {
            val isStartHandle = x?.startsWith(START) == true
            if (isStartHandle) {
                //开始计时
                LogMonitor.instance.startMonitor()
                start = System.currentTimeMillis()
            }
            if (x?.startsWith(END) == true) {
                //结束计时,并计算出方法执行时间
                LogMonitor.instance.removeMonitor()
                val end = System.currentTimeMillis()
                val diff = end - start
                if (diff > 100 && start > 0) {
                    Log.e(TAG, "dq-handle忙死了,Please check your task,time=$diff" + "ms")
                }
            }
        }
    }

帧率统计

系统每一帧的绘制都有回调,我们也是可以注册监听。通过Choreographer来统计帧率:

@JvmStatic
    fun frameMonitor() {//帧率检测
        mFpsCount = 0
        mLastFrameTime = 0
        mLastFrameTimeNanos = 0
        mFrameTimeNanos = 0
        Choreographer.getInstance().postFrameCallback(object : FrameCallback {
            override fun doFrame(frameTimeNanos: Long) {
                val diff = (frameTimeNanos - mLastFrameTimeNanos) / 1_000_000
                if (diff > frameCountPerSecond && mLastFrameTimeNanos > 0) {
                    val droppedFrames = (diff / frameCountPerSecond).roundToInt()
                    if (droppedFrames > 3) {
                        Log.d(TAG, "dq-丢帧 droppedFrames=$droppedFrames")
                    }
                }
                mLastFrameTimeNanos = frameTimeNanos
                mFpsCount++
                mFrameTimeNanos = frameTimeNanos
                Choreographer.getInstance().postFrameCallback(this)
            }
        })

        Rx2Utils.backgroundTaskDelayed(runnable, 1000)
    }

打印耗时堆栈

handler任务开始(>>>>> Dispatching to)时,设置定时器,如果handle处理耗时超过100ms,打印主线程的堆栈。

/**
         * 自定义超时的阈值,如果handle处理耗时超过100ms,打印堆栈
         */
        private const val TIME_BLOCK = 100L
        private val mLogRunnable = Runnable {
            //打印出执行的耗时方法的栈消息
            val sb = StringBuilder()
            val stackTrace = Looper.getMainLooper().thread.stackTrace
            for (s in stackTrace) {
                sb.append(s.toString())
                sb.append("\n")
            }
            Log.e(TAG, "dq-main-ui stackTrace=$sb")
        }

Janky Frames

官方衡量Janky frames的标准:一帧的时间超过16.67ms。可以通过adb命令获取当前的丢帧信息。

adb shell dumpsys gfxinfo your_package_name framestats

Matrix检测

使用Matrix框架,可以统计并打印每个方法的耗时,有对主要耗时的方法降序打印堆栈信息。

系统UI工具

开发者模式里面打开帧率统计工具


image.png

profile工具

as自带的profile工具,可以看方法耗时


profile工具.png

Thanks

Welcome to contact me: [email protected] or Wechat:dusan2010

你可能感兴趣的:(Android性能优化:UI卡顿、帧率检测&优化)