一. Android渲染常识

1.1 绘制原理

Android零碎要求每一帧都要在 16ms 内绘制实现,平滑的实现一帧意味着任何非凡的帧须要执行所有的渲染代码(包含 framework 发送给 GPU 和 CPU 绘制到缓冲区的命令)都要在 16ms 内实现,放弃晦涩的体验。这个速度容许零碎在动画和输出事件的过程中以约 60 帧每秒( 1秒 / 0.016帧每秒 = 62.5帧/秒 )的平滑帧率来渲染。

如果你的利用没有在 16ms 内实现这一帧的绘制,假如你花了 24ms 来绘制这一帧,那么就会呈现掉帧的状况。

零碎筹备将新的一帧绘制到屏幕上,然而这一帧并没有筹备好,所有就不会有绘制操作,画面也就不会刷新。反馈到用户身上,就是用户盯着同一张图看了 32ms 而不是 16ms ,也就是说掉帧产生了。

1.2 掉帧

掉帧是用户体验中一个十分外围的问题。抛弃了以后帧,并且之后不可能连续之前的帧率,这种不间断的距离会容易会引起用户的留神,也就是咱们常说的卡顿、不晦涩。引起掉帧的起因十分多,比方:

  • 花了十分多工夫从新绘制界面中的大部分货色,这样十分节约CPU周期;
  • 适度绘制重大,在绘制用户看不到的对象上破费了太多的工夫;
  • 有一大堆动画反复了一遍又一遍,耗费 CPU 、 GPU 资源;
  • 频繁的触发垃圾回收;

1.3 为什么是60Fps?

Android零碎要求每一帧都要在 16ms 内绘制实现,那么1秒的帧率就是约 60 帧每秒( 1秒 / 0.016帧每秒 = 62.5帧/秒 ),那为什么要以 60 Fps来作为 App 性能的衡量标准呢?这是因为人眼和大脑之间的合作无奈感知到超过 60 Fps的画面更新。

市面上绝大多数Android设施的屏幕刷新频率是 60 HZ。当然,超过 60 Fps 是没有意义的,人眼感知不到区别。24 Fps 是人眼能感知的间断线性的静止,所以是电影胶圈的罕用帧率,因为这个帧率曾经足够撑持大部分电影画面所要表白的内容,同时能最大限度地缩小费用收入。然而,低于 30 Fps 是无奈顺畅体现壮丽的画面内容的,此时就须要用到 60 Fps 来达到想要表白的成果。

利用的界面性能指标就是放弃 60 Fps,这意味着每一帧你只有 16 ms(1秒 / 60帧率)的工夫来解决所有的工作。

1.4 垃圾回收

垃圾回收器是一个在利用运行期间主动开释那些不再援用的内存的机制,常称 GC 。频繁的 GC 也是导致重大性能问题的罪魁祸首之一。

后面提到,平滑的实现一帧意味着所有渲染代码都必须在 16ms 内实现。频繁的 GC 会重大限度一帧工夫内的剩余时间,如果 GC 所做的工作超过了那些必须的工作,那么留给利用平滑的帧率的工夫就越少。越靠近 16ms ,在垃圾回收事件触发的时候,就越容易导致卡顿。

留神,Android4.4 引进了新的 ART 虚拟机来取代 Dalvik 虚拟机。它们的机制大有不同,简略而言:

  • Dalvik 虚拟机的 GC 是十分耗资源的,并且在失常的状况下一个硬件性能不错的Android设施也会很容易消耗掉 10 – 20 ms 的工夫;
  • ART 虚拟机的GC会动静晋升垃圾回收的效率,在 ART 中的中断,通常在 2 – 3 ms 间。 比 Dalvik 虚拟机有很大的性能晋升;

ART 虚拟机绝对于 Dalvik 虚拟机来说的垃圾回收来说有一个很大的性能晋升,但 2 – 3 ms 的回收工夫对于超过16ms帧率的界线也是足够的。因而,只管垃圾回收在 Android 5.0 之后不再是耗资源的行为,但也是始终须要尽可能防止的,特地是在执行动画的状况下,可能会导致一些让用户显著感觉的丢帧。

1.5 UI 线程

UI 线程是利用的主线程,很多的性能和卡顿问题是因为咱们在主线程中做了大量的工作。所以,所有耗资源的操作,比方 IO 操作、网络操作、SQL 操作、列表刷新等,都应该用后盾过程去实现,不能占用主线程,主线程是 UI 线程,是放弃程序晦涩的要害;

在 Android 5.0 版本里,Android 框架层引入了 “ Render Thread ” ,用于向 GPU 发送理论渲染的操作。这个线程加重了一些 UI 线程缩小的操作。然而输出、滚动和动画依然在 UI thread,因为 Thread 必须可能响应操作。

1.6 垂直同步

垂直同步是 Android4.1 通过 Project Butter 在 UI 架构中引入的新技术,同期引入的还有 Triple Buffer 和 HWComposer 等技术,都是为进步 UI 的流畅性而生。

一般而言, GPU 的帧速率应高于刷新率,才不会卡顿或掉帧。如果屏幕刷新率比帧速率还快,屏幕会在两帧中显示同一个画面,这种断断续续状况继续产生时,用户将会很显著地感觉到动画的卡顿或者掉帧,而后又恢复正常,咱们常称之为闪屏、跳帧、提早。利用应防止这些帧率降落的状况,以确保 GPU 能在屏幕刷新之前实现数据的获取及写入,保障动画晦涩。

1.7 UI 绘制机制与栅格化

绝大多数渲染操作都依赖两个硬件: CPU 、 GPU 。 CPU 负责 Measure 、 layout 、 Record 、 Execute 的计算操作, GPU 负责栅格化( Rasterization )操作。 非必须的视图组件会带来多余的 CPU 计算操作,还会占用多余的 GPU 资源。

栅格化( Rasterization )能将 Button 、 Shape 、 Path 、 Bitmap 等资源组件拆分到不同的像素上进行显示。这个操作很费时,所以引入了 GPU 来放慢栅格化的操作。

CPU 负责把 UI 组件计算成多边形( Polygons ),纹理( Texture ),而后交给 GPU 进行栅格化渲染,再将处理结果传到屏幕上显示。

在 Android 里的那些资源组件的显示(比方 Bitmaps 、 Drawable ),都是一起打包到对立的纹理( Texture )当中,而后再传递到 GPU 外面。

图片的显示,则是先通过 CPU 的计算加载到内存中,再传给 GPU 进行渲染。

文字的显示,则是先通过 CPU 换算成纹理( Texture ),再传给 GPU 进行渲染,返回到 CPU 绘制单个字符的时候,再从新援用通过 GPU 渲染的内容。

动画的显示更加简单,咱们须要在 16 ms 内解决完所有 CPU 和 GPU 的计算、绘制、渲染等操作,能力取得利用的晦涩体验。

二. To检测和解决

2.1 检测维度

依据业务的不同与所须要的测试粒度的不同,就会有不同的检测维度。目前我所在业务所需的界面性能检测维度如下:

  • 界面适度绘制;(检测适度绘制)
  • 渲染性能;(检测严格模式下的UI渲染性能出现)
  • 布局边界合理性;(检测元素显示的合理性)

还有专项测试中某些用户场景可能还蕴含着另外一些隐形的检测维度,比方:

  • OpenGL 跟踪剖析;
  • GPU 视图更新合理性;
  • Flash 硬件层更新合理性;
  • 动画加 / 加速状态问题点检测;
  • ……

2.2 调试工具

检测和解决界面性能问题很大水平上依赖于你的应用程序架构,侥幸的是,Andorid 提供了很多调试工具,晓得并学会应用这些工具很重要,它们能够帮忙咱们调试和剖析界面性能问题,以让利用领有更好的性能体验。上面列举Android常见的界面性能调试工具:

2.2.1 Hierarchy View

Hierarchy View 在Android SDK里自带,罕用来查看界面的视图构造是否过于简单,用于理解哪些视图适度绘制,又该如何进行改良。

2.2.2 Lint

Lint 是 ADT 自带的动态代码扫描工具,能够给 XML 布局文件和 我的项目代码中不合理的或存在危险的模块提出改善性倡议。官网对于 Lint 的理论应用的提醒,列举几点如下:

  • 蕴含无用的分支,倡议去除;
  • 蕴含无用的父控件,倡议去除;
  • 正告该布局深度过深;
  • 倡议应用 compound drawables ;
  • 倡议应用 merge 标签;
  • ……

2.2.3 Systrace

Systrace 在Android DDMS 里自带,能够用来跟踪 graphics 、view 和 window 的信息,发现一些深层次的问题。很麻烦,限度大,理论调试中我根本用不到。

2.2.4 Track

Track 在 Android DDMS里自带,是个很棒的用来跟踪结构视图的时候哪些办法费时,准确到每一个函数,无论是利用函数还是零碎函数,咱们能够很容易地看到掉帧的中央以及那一帧所有函数的调用状况,找出问题点进行优化。

2.2.5 OverDraw

通过在 Android 设施的设置 APP 的开发者选项里关上 “ 调试 GPU 适度绘制 ” ,来查看利用所有界面及分支界面下的适度绘制状况,不便进行优化。

2.2.6 GPU 出现模式分析

通过在 Android 设施的设置 APP 的开发者选项里启动 “ GPU 出现模式分析 ” ,能够失去最近 128 帧 每一帧渲染的工夫,剖析性能渲染的性能及性能瓶颈。官网介绍 「戳我」。

2.2.7 StrictMode

通过在 Android 设施的设置 APP 的开发者选项里启动 “ 严格模式 ” ,来查看利用哪些操作在主线程上执行工夫过长。当一些操作违反了严格模式时屏幕的周围边界会闪动红色,同时输入 StrictMode 的相干信息到 LOGCAT 日志中。

2.2.8 Animator duration scale

通过在 Android 设施的设置 APP 的开发者选项里关上 “ 窗口动画缩放 ” / “ 过渡动画缩放 ” / “ 动画程序时长缩放 ”,来减速或减慢动画的工夫,以查看减速或减慢状态下的动画是否会有问题。

2.2.9 Show hardware layer updates

通过在 Android 设施的设置 APP 的开发者选项里启动 “ 显示硬件层更新 ”,当 Flash 硬件层在进行更新时会显示为绿色。应用这个工具能够让你查看在动画期间哪些不冀望更新的布局有更新,不便你进行优化,以取得利用更好的性能。实例《 Optimizing Android Hardware Layers 》

2.3 如何解决

后面提到过我司的目前所需的测试维度如下:

  • 界面适度绘制;(检测适度绘制)
  • 渲染性能;(检测严格模式下的UI渲染性能出现)
  • 布局边界合理性;(检测元素显示的合理性)

故接下来将围绕这三两点,别离从概念、追踪、开掘本源以及排查的工具来具体讲述如何解决,以及给开发的优化倡议。

三. 界面适度绘制(OverDraw)

3.1 适度绘制概念

适度绘制是一个术语,示意某些组件在屏幕上的一个像素点的绘制次数超过 1 次。

艰深来讲,绘制界面能够类比成一个涂鸦客涂鸦墙壁,涂鸦是一件工作量很大的事件,墙面的每个点在涂鸦过程中可能被涂了各种各样的色彩,但最终出现的色彩却只可能是 1 种。这意味着咱们花大力量涂鸦过程中那些非最终出现的色彩对路人是不可见的,是一种对工夫、精力和资源的节约,存在很大的改善空间。绘制界面同理,花了太多的工夫去绘制那些重叠在上面的、用户看不到的货色,这样是在节约CPU周期和渲染工夫!

官网例子,被用户激活的卡片在最下面,而那些没有激活的卡片在上面,在绘制用户看不到的对象上破费了太多的工夫。

3.2 追踪适度绘制

通过在 Android 设施的设置 APP 的开发者选项里关上 “ 调试 GPU 适度绘制 ” ,来查看利用所有界面及分支界面下的适度绘制状况,不便进行优化。

Android 会在屏幕上显示不同深浅的色彩来示意适度绘制:

  • 没色彩:没有适度绘制,即一个像素点绘制了 1 次,显示利用原本的色彩;
  • 蓝色:1倍适度绘制,即一个像素点绘制了 2 次;
  • 绿色:2倍适度绘制,即一个像素点绘制了 3 次;
  • 浅红色:3倍适度绘制,即一个像素点绘制了 4 次;
  • 深红色:4倍适度绘制及以上,即一个像素点绘制了 5 次及以上;

设施的硬件性能是无限的,当适度绘制导致利用须要耗费更多资源(超过了可用资源)的时候性能就会升高,体现为卡顿、不晦涩、ANR 等。为了最大限度地进步利用的性能和体验,就须要尽可能地缩小适度绘制,即更多的蓝色色块而不是红色色块。

理论测试,罕用以下两点来作为适度绘制的测试指标,将适度绘制管制在一个约定好的正当范畴内:

  • 利用所有界面以及分支界面均不存在超过4X适度绘制(深红色区域);
  • 利用所有界面以及分支界面下,3X适度绘制总面积(浅红色区域)不超过屏幕可视区域的1/4;

3.3 适度绘制的本源

适度绘制很大水平上来自于视图互相重叠的问题,其次还有不必要的背景重叠。

官网例子,比方一个利用所有的View都有背景的话,就会看起来像第一张图中那样,而在去除这些不必要的背景之后(指的是Window的默认背景、Layout的背景、文字以及图片的可能存在的背景),成果就像第二张图那样,根本没有适度绘制的状况。

3.4 不合理的xml布局对绘制的影响

当布局文件的节点树的深度越深,XML 中的标签和属性设置越多,对界面的显示有灾难性影响。

一个界面要显示进去,第一步会进行解析布局,在 requestLayout 之后还要进行一系列的 measure 、 layout 、 draw 操作,若布局文件嵌套过深、领有的标签属性过于臃肿,每一步的执行工夫都会受到影响,而界面的显示是进行完这些操作后才会显示的,所以每一步操作的工夫增长,最终显示的工夫就会越长。

四. 渲染性能(Rendering)

4.1 渲染性能概念

渲染性能往往是掉帧的罪魁祸首,这种问题很常见,让人头疼。好在 Android 给咱们提供了一个弱小的工具,帮忙咱们非常容易追踪性能渲染问题,看到到底是什么导致你的利用呈现卡顿、掉帧。

4.2 追踪渲染性能

通过在 Android 设施的设置 APP 的开发者选项里关上 “ GPU 出现模式分析 ” 选项,抉择 ” 在屏幕上显示为条形图 “ 。

这个工具会在Android 设施的屏幕上实时显示以后界面的最近 128 帧 的 GPU 绘制图形数据,包含 StatusBar 、 NavBar 、 以后界面的 GPU 绘制图形柱状图数据。咱们个别只需关怀以后界面的 GPU 绘制图形数据即可。

界面上一共有 128 个小柱状图,代表的是以后界面最近的 128 帧 GPU 绘制图形数据。一个小柱状图代表的这一帧画面渲染的耗时,柱状图越高代表耗时越长。随着界面的刷新,柱状图信息也会实时滚动刷新。

两头有一条绿线,代表 16 ms ,放弃动画晦涩的要害就在于让这些垂直的柱状条尽可能地放弃在绿线上面,任何时候超过绿线,你就有可能失落一帧的内容。

每一个柱状图都是由三种色彩形成:蓝、红、黄。

  • 蓝色代表的是这一帧绘制 Display List 的工夫。艰深来说,就是记录了须要破费多长时间在屏幕上更新视图。用代码语言来说,就是执行视图的 onDraw 办法,创立或更新每一个视图的 Display List 的工夫。
  • 红色代表的是这一帧 OpenGL 渲染 Display List 所须要的工夫。艰深来说,就是记录了执行视图绘制的耗时。用代码语言来说,就是 Android 用 OpenGL ES 的 API 接口进行 2D 渲染 Display List 的工夫。
  • 黄色代表的是这一帧 CPU 期待 GPU 解决的工夫。艰深来说,就是 CPU 期待 GPU 收回接到命令的回复的等待时间。用代码语言来说,就是这是一个阻塞调用。

理论测试,罕用以下两点来作为渲染性能的测试指标,将渲染性能管制在一个约定好的正当范畴内:

  • 执行利用的所有性能及分支性能,操作过程中波及的柱状条区域应至多 90 % 放弃到绿线上面;
  • 从用户体检的角度主观判断利用在 512 M 内存的 Android 设施下所有操作过程中的卡顿感是否能承受,不会感觉突兀怪异;

4.3 渲染性能差的本源

当你看到蓝色的线较高的时候,可能是因为你的视图忽然有效了须要从新绘制,或者是自定义的视图过于简单耗时过长。

当你看到红色的线较高的时候,可能是因为你的视图从新提交了须要从新绘制导致的(比方屏幕从竖屏旋转成横屏后以后界面从新创立),或者是自定义的视图很简单,绘制起来很麻烦,导致耗时过长。比方上面这种视图:

当你看到黄色的线较高的时候,那就意味着你给 GPU 太多的工作,太多的负责视图须要 OpenGL 命令去绘制和解决,导致 CPU 迟迟没等到 GPU 收回接到命令的回复。

4.4 检测阐明

这个工具可能很好地帮忙你找到渲染相干的问题,帮忙你找到卡顿的性能瓶颈,追踪到底是什么导致被测利用呈现卡顿、变慢的状况,以便在代码层面进行优化。甚至让负责产品设计的人去改善他的设计,以取得良好的用户体验。

检测渲染性能时,常随同着开启“ 严格模式 ” 查看利用哪些情景在 UI 线程(主线程)上执行工夫过长。

另外有些弱小但可能少用的工具在测试性能渲染时辅助剖析,比方:

  • HierarchyViewer:这个工具罕用来查看界面的视图构造是否过于简单,用于理解哪些视图适度绘制,又该如何进行改良;
  • Tracer for OpenGL:这个工具收集了所有UI界面发给GPU的绘制命令。罕用于辅助开发人员 DEBUG 、定位一些 HierarchyViewer 工具定位不了的疑难渲染细节问题。

4.5 UI绘制机制的补充阐明

如下面所说,布局和 UI 组件等都会先通过 CPU 计算成 GPU 可能辨认并绘制的多边形( Polygons ),纹理( Texture ),而后交给 GPU 进行栅格化渲染,再将处理结果传到屏幕上显示。 “ CPU 计算成 GPU 可能辨认并绘制的对象 ” 这个操作是在 DisplayList 的帮忙下实现的。DisplayList 领有要交给 GPU 栅格化渲染到屏幕上的数据信息。

DisplayList 会在某个视图第一次须要渲染时创立。当该视图有相似地位被挪动等变动而须要从新渲染这个视图的时候,则只需 GPU 额定执行一次渲染指令冰更新到屏幕上就够了。但如果视图中的绘制内容发生变化时(比方不可见了),那之间的 DisplayList 就无奈持续应用了,这时零碎就会从新执行一次从新创立 DisplayList 、渲染DisplayList 并更新到屏幕上。这个流程的体现性能取决于该视图的复杂程度。

六. 给开发的界面优化 Advice

6.1 优化布局的构造

布局构造太简单,会减慢渲染的速度,造成性能瓶颈。咱们能够通过以下这些习用、无效的布局准则来优化:

  • 防止简单的View层级。布局越简单就越臃肿,就越容易呈现性能问题,寻找最节俭资源的形式去展现嵌套的内容;
  • 尽量避免在视图层级的顶层应用绝对布局 RelativeLayout 。绝对布局 RelativeLayout 比拟耗资源,因为一个绝对布局 RelativeLayout 须要两次度量来确保本人解决了所有的布局关系,而且这个问题会随同着视图层级中的绝对布局 RelativeLayout 的增多,而变得更重大;
  • 布局层级一样的状况倡议应用线性布局 LinearLayout 代替绝对布局 RelativeLayout,因为线性布局 LinearLayout 性能要更高一些;的确须要对分支进行绝对布局 RelativeLayout 的时候,能够思考更优化的网格布局 GridLayout ,它曾经预处理了分支视图的关系,能够防止两次度量的问题;
  • 绝对简单的布局倡议采纳绝对布局 RelativeLayout ,绝对布局 RelativeLayout 能够简略实现线性布局 LinearLayout 嵌套能力实现的布局;
  • 不要应用相对布局 AbsoluteLayout ;
  • 将可重复使用的组件抽取进去并用 标签进行重用。如果利用多个中央的 UI 用到某个布局,就将其写成一个布局部件,便于各个 UI 重用。官网详解 「 戳我 」
  • 应用 merge 标签缩小布局的嵌套档次,官网详解 「 戳我 」;
  • 去掉多余的不可见背景。有多层背景色彩的布局,只留最上层的对用户可见的色彩即可,其余用户不可见的底层色彩能够去掉,缩小有效的绘制操作;
  • 尽量避免应用 layoutweight 属性。应用蕴含 layoutweight 属性的线性布局 LinearLayout 每一个子组件都须要被测量两次,会耗费过多的系统资源。在应用 ListView 标签与 GridView 标签的时候,这个问题显的尤其重要,因为子组件会反复被创立。平分布局能够应用绝对布局 RelativeLayout 里一个 0dp 的 view 做分割线来搞定,如果不行,那就……;
  • 正当的界面的布局构造应是宽而浅,而不是窄而深;

6.2 优化解决逻辑

  • 按需载入视图。某些不怎么重用的耗资源视图,能够等到须要的时候再加载,进步UI渲染速度;
  • 应用 ViewStub 标签来加载一些不罕用的布局;
  • 动静地 inflation view 性能要比用 ViewStub 标签的 setVisiblity 性能要好,当然某些性能的实现采纳 ViewStub 标签更适合;
  • 尽量避免不必要的耗资源操作,节俭贵重的运算工夫;
  • 防止在 UI 线程进行沉重的操作。耗资源的操作(比方 IO 操作、网络操作、SQL 操作、列表刷新等)耗资源的操作利用后盾过程去实现,不能占用 UI 线程,UI 线程是主线程,主线程是放弃程序晦涩的要害,应该只操作那些外围的 UI 操作,比方解决视图的属性和绘制;
  • 最小化唤醒机制。咱们罕用播送来接管那些冀望响应的音讯和事件,但过多的响应超过自身需要的话,会耗费多余的 Android 设施性能和资源。所以应该最小化唤醒机制,当利用不关怀这些隐没和事件时,就敞开播送,并谨慎抉择那些要响应的 Intent 。
  • 为低端设施思考,比方 512M 内存、双核 CPU 、低分辨率,确保你的利用能够满足不同程度的设施。
  • 优化利用的启动速度。当利用启动一个利用时,界面的尽快反馈显示能够给用户一个良好的体验。为了启动更快,能够提早加载一些 UI 以及防止在利用 Application 层级初始化代码。

6.3 善用 DEBUG 工具

  • 多应用Android提供的一些调试工具去追踪利用次要性能的性能状况;
  • 多应用Android提供的一些调试工具去追踪利用次要性能的内存分配情况;

    #### 相干视频举荐:

【2021最新版】Android studio装置教程+Android(安卓)零基础教程视频(适宜Android 0根底,Android初学入门)含音视频_哔哩哔哩_bilibili

【 Android进阶教程】——App启动速度的优化_哔哩哔哩_bilibili

Android进阶零碎学习——高级UI卡顿性能优化_哔哩哔哩_bilibili