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: duqian2010@gmail.com or Wechat:dusan2010

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容

  • 卡顿是非常直观的用户体验,它的特点是:产生原因错综复杂,线上问题难以复现。基于这个特点,卡顿优化主要是三方面工作:...
    Stan_Z阅读 15,414评论 6 29
  • 在移动APP性能评测-流畅度评测中,我们介绍了如何准确客观评价APP的流畅度,最终采用SM指标来评价应用的流畅度,...
    htkeepmoving阅读 12,326评论 0 22
  • 大多数用户感知到的卡顿等性能问题主要原因都是因为渲染性能。 Android 系统每隔大概16.6毫秒(1000ms...
    初夏的雪阅读 894评论 0 3
  • Android 卡顿研究 [TOC] 稳定化,不是说说而已 基础概念 这里主要是根据张绍文老师的文章做的笔记,根据...
    DoneWillianm阅读 2,810评论 1 21
  • 如何定义发生了卡顿现象: 线下很难复现,与发生场景强相关(所以需要我们去做卡顿监控,收集现场信息) CPU相关知识...
    今阳说阅读 657评论 0 2