性能优化技巧知识梳理(3) - 如何排查列表卡顿问题

一、前言

如果现在用户反馈某个列表很卡,你会怎么排查问题?

这样一个简短的问题,其实考察了我们多方面的知识。要答出其中的一两个小点其实并不难,难的是如何能够由外之内,由浅入深娓娓道来,它考察的是一个程序员发现问题、解决问题、归纳总结的能力。

要回答这个问题,可以从以下四个方面层层深入,整个大纲如下:

  • (1) 渲染原理
    • 为什么会感知到卡顿
    • 理解VSYNC
  • (2) 卡顿的外部因素
    • 手机性能
    • 系统本身
    • 内存抖动
    • 在主线程执行耗时操作
  • (3) 卡顿的内部因素
    • 布局层级
    • measurelayoutdraw耗时
    • 过度绘制
  • (4) 监控卡顿
    • 使用Handler#setMessageLogging

这篇文章中穿插着介绍了性能优化工具的使用场景,所有的链接地址为:

二、渲染原理

首先我们需要明白 为什么用户会感知到卡顿,要回答这个问题,就需要对渲染的原理有一个基本的了解。

2.1 为什么会感知到卡顿

用户感知到的卡顿主要的根源是因为渲染性能。Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到所需要的60fps,为了能够实现60fps,这意味着程序的大多数操作都必须在16ms内完成。

如果你的某个操作是24ms,系统在得到VSYNC信号的时候就无法进行正常的渲染,这样就发生了丢帧现象,那么用户在32ms内看到的是同一帧画面。

2.2 理解 VSYNC

在理解VSYNC之前,首先需要区分 帧率刷新率

  • 帧率:代表了GPU1s绘制操作 的帧数,例如30fps60fps,属于 软件参数
  • 刷新率:代表了屏幕在1s内刷新屏幕的次数,这取决于 硬件的固定参数,例如60Hz

GPU获取图形数据进行渲染,然后硬件负责把渲染后的内容呈现到屏幕上,两者不停地协作。

当帧率和刷新率不一致的时候,就会发生画面上下两部分内容断裂,来自不同的两帧数据发生重叠。因此引入了VSYNC,在超过60fps的情况下,GPU所产生的帧数据会因为等待VSYNC的刷新信号而被Hold住,这样能够保持每次刷新都有实际的新的数据可以显示。

但是我们遇到更多的情况是 帧率小于刷新率,也就是我们通常所说的卡顿,如下图所示。

三、卡顿的外部因素

卡顿的 外因 可以归结为以下几个方面:

  • 手机性能问题,CPU性能不足,内存小。
  • 系统本身问题,所有应用都很卡。
  • 频繁触发GC,导致内存抖动。
  • 在主线程中进行了耗时的操作。

3.1 手机性能问题

通过排查反馈用户的机型,如果大部分的反馈都是来自于低端机的用户,那么可以与产品沟通,通过获取硬件的相关参数,例如CPU核数、内存大小,对于这些低端机型进行特殊的处理,对需求进行简化,避免去实现复杂的动画效果。

3.2 系统本身问题

如果可以联系用户,并且用户反馈不仅是我们的应用,而是整个系统都很卡,那么对于我们来说,其实做不了什么。

如果无法联系用户,那么可以通过trace文件进行分析。

3.3 内存抖动

内存抖动指的是有大量的对象频繁地进出内存的新生代区域,它往往会伴随着频繁的GC,而GC会占用UI线程和CPU资源,从而导致应用发生卡顿,因此我们需要尽量这种现象的发生。

3.3.1 排查内存抖动

在排查内存抖动问题的时候,我们可以通过以下几个工具来辅助排查问题:

  • Memory Monitor:在列表滑动的时候,实时观察内存的分配情况,定位发生GC的时间点,确定其是否合理,但是其缺点是 无法列出具体的分配对象
  • Heap Viewer:在垃圾回收的时候,呈现出某一时刻的内存快照,帮助我们分析是哪个对象引起了内存泄漏。
  • Allocation Tracker:分析出一段时间内对象的分配情况,并列出是由什么逻辑导致了这个对象的分配,与Heap Viewer配合使用,来分析大对象产生的原因。

以上这三种工具的详细使用可以看之前总结的这篇文章:性能优化工具知识梳理(6) - Memory Monitor & Heap Viewer & Allocation Tracker

3.3.2 容易发生内存抖动的场景

在平时的开发中,我们可以使用以下几点来避免内存抖动的发生:

  • 在创建对象的操作,移出到循环体外
  • 不要在onMeasureonLayoutonDraw方法中频繁地创建对象,例如PaintPath这样的类。
  • 在使用Bitmap的时候,考虑通过LruCache+inBitmap的方式进行复用。
  • 合理地使用对象池来缓存对象。

3.4 在主线程中,执行了耗时的操作

3.4.1 排查耗时操作

在排查主线程的耗时操作时,最常用的就是TraceView,通过这个工具可以看到每个方法的具体耗时时间,关于TraceView的详细使用可以参考 性能优化工具知识梳理(1) - TraceView 这篇文章。

3.4.2 解决主线程耗时问题

在解决主线程耗时问题时,需要根据具体的业务的场景来排查,一般来说,当我们遇到列表卡顿的问题,可以优先从以下几个重要的回调中排查,看下是否在其中执行了耗时的操作,例如IOJSON等。

  • RecyclerViewonBindViewHolder
  • ListViewgetView
  • RecyclerView/ListViewonScrollChanged

四、卡顿的内部因素

  • 布局层级
  • measurelayoutdraw的耗时
  • 过度绘制

4.1 布局层级

当我们设计列表的每个Item项时,应当尽量减少每个Item的布局层级,因为布局层级越深,每个Item绘制就越耗时。

4.1.1 排查布局层级问题

在检查布局层级问题时,通常是使用Hierarchy Viewer工具,通过该工具可以做到以下两点:

  • 检查每个Item项的布局层级
  • 通过每个节点的三个圆点颜色查看其在测量、布局、绘制三个阶段的性能表现,绿色表示OK,黄色表示其处于渲染速度比较慢的50%,红色表示渲染速度非常慢。

更加详细的介绍可以参考 性能优化工具知识梳理(4) - Hierarchy Viewer

4.1.2 减少布局层级

减少布局层级更多的是需要依赖开发者的习惯,因为有些时候,越少的层级往往需要更复杂的设计逻辑,这意味着需要花更多的时间来思考,在这里强烈推荐ConstraintLayout控件,对于任何复杂的场景,只需要一层就可以了,使用可以参考 ConstraintLayout 完全解析 快来优化你的布局吧

对于减少布局层级,有以下几点技巧:

  • 首先应当考虑布局层级最小的方案。
  • 布局层级相同时,就应当选取合适的父容器,一般来说,有以下几点经验:
  • 选取的优先级为:FrameLayout、不带layout_weight参数的LinearLayoutRelativeLayout,这里选取的标准为带有layout_weightLinearLayout或者RelativeLayout会测量两次。
  • 当使用LinearLayout时,应当尽量避免使用layout_weight参数。
  • 避免使用RelativeLayout嵌套RelativeLayout
  • 如果允许,那么可以使用GoogleConstraintLayout布局。

更多的技巧可以参考 性能优化技巧知识梳理(1) - 布局优化

4.2 measure、layout、draw 的耗时时间

对于这三个阶段的耗时,可以通过两个工具来排查问题:

当我们发现在某个阶段耗时过长时,就需要去排查是否在以上三个回调当中做了不当的操作。

4.3 过度绘制

过度绘制其实是 布局层级过深的结果,通过设置中的 调试 GPU 过度绘制,可以直观地看到绘制的重叠情况,检测的结果分为以下四种,严重程度依次递增:

  • 蓝色
  • 绿色
  • 浅红
  • 深红

对于过度绘制的部分,需要想办法去优化,详细的使用方式为:性能优化工具知识梳理(3) - 调试GPU过度绘制 & GPU呈现模式分析

五、监控卡顿

在前面两节中,我们从外因和内因两个部分总结了卡顿问题的排查方法和注意事项,除此之外,还可以通过一些手段实时地监控卡顿问题,这里推荐使用HandlersetMessageLogging方法,检测每个消息的耗时时间,当其耗时大于阈值的时候,输出堆栈信息。

简单的实现方式为 Framework 源码解析知识梳理(4) - 从源码角度谈谈 Handler 的应用

BlockCanary就是基于这个原理来实现的,具体的使用方式可以参考 AndroidPerformanceMonitor

六、参考文献

1. Android 性能优化典范 - 第1季
2. ConstraintLayout 完全解析 快来优化你的布局吧

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

推荐阅读更多精彩内容