2019独角兽企业重金招聘Python工程师标准>>>

前言

本篇文章是udacity上的Android Performace系列视频-Render篇的课程纪要。

这个系列是视频是Google和udacity合作推出的视频,在Google的Android Performace Pattern系列视频的基础上加上了练习的章节,同时还有有趣的过场情节,非常不错的视频。

课程地址:

https://cn.udacity.com/course/android-performance--ud825

目录

  • Draw!
  • Android系统的渲染组件
  • 格栅化 - Rasterization
  • 过度绘制 - Overdraw
  • 避免Overdraw
  • Layouts, Invalidation And Performance
  • 布局分析工具 - Hierarchy Viewer
  • 优化布局
  • 总结

DRAW!

Android系统每隔16ms重新绘制一次Activity,也就是说必须在16ms之内完成屏幕刷新的全部逻辑操作,这样才能达到每秒60帧。

PS:每秒60帧是手机硬件决定的,现在大多数手机屏幕刷新频率在60Hz。1000ms/60Hz = 16.666ms/frame。

如果错过了16ms,比如花费了24ms才完成计算,那么就会出现丢帧的情况。

系统准备刷新,但界面还没有准备好,所以用户盯着同一张图看了32ms而不是16ms,丢帧情况下的动画会让用户觉得卡顿。下面让我们来看看造成卡顿的原因以及如何去解决应用中的这些问题。

Android系统的渲染组件

Android系统的渲染Pipeline分为两个关键组件,CPUGPU,两者共同工作在屏幕上绘制图片。每个组件都有自身的特定流程,必须遵守这些特定操作规则才能达到效果。

GPU方面,最常见的性能问题是“过度绘制Overdraw”

CPU方面,最常见的性能问题是“布局的渲染效率”

本文我们将一一介绍,以及如何使用SDK中的可用工具找出拖累应用性能的原因。

格栅化 - Rasterization

在介绍更多之前,我们需要先了解一些底层的机制。Android是如何将复杂的XML布局文件和标记语言转换成用户能看懂的图像的。实际上,这是由格栅化操作来完成的。

格栅化将诸如字符串String、按钮Button、路径Path或形状Shape的一些高级对象拆分到不同的像素上在屏幕上进行显示。格栅化是一个非常费时的操作。GPU的引入就是为了加快格栅化。

GPU使用一些指定的基础指令集来绘制多边形Polygons和纹理Textur,CPU通过OpenGL ES向GPU输入这些指令。

CPU负责把UI组件计算成多边形Polygons或纹理Texture,然后交给GPU进行栅格化渲染。

UI组件转化成一系列多边形和纹理的过程相当耗时、CPU将数据传输给GPU也是非常耗时的动作

所以很明显,必须要减少转换的次数已经上传数据的次数。幸亏OpenGL ES API允许数据上传到GPU后可以对数据进行保存,当下一次绘制一个按钮时,只需要在GPU memory中引用它,然后告诉OpenGL如何绘制。

从底层的角度来看,渲染性能的优化就是尽可能快地上传数据到GPU,然后尽可能地在不修改的条件下保存数据。

Android Honeycomb之后,整个UI渲染系统就在GPU中运行。Android系统在降低reduce、重新利用reuse和回收recycle GPU资源方面做了很多工作。

比如任何由主题所提供的资源,例如Bitmaps、Drawables等都是一起打包到统一的纹理当中,然后使用网格工具上传到GPU,比如Nine Patches。这样每次你需要绘制这些资源时,都是直接从纹理里面进行获取渲染的,大大加快了这些视图类型的显示。

当然随着UI组件的越来越丰富,有了更多演变的形态。例如显示图片的时候,需要先经过CPU的计算加载到内存中,然后传递给GPU进行渲染。文字的显示更加复杂,需要先经过CPU换算成纹理,然后再交给GPU进行渲染,回到CPU绘制单个字符的时候,再重新引用经过GPU渲染的内容。动画则是一个更加复杂的操作流程。

好了,在了解了GPU绘制的底层原理后,我们来看一个GPU性能的问题瓶颈 - 过度绘制Overdraw。

过度绘制 - Overdraw

过度渲染指的是屏幕上的某个像素点在同一帧的时间内被绘制了多次。有些看不见的布局也进行了绘制,这样就会浪费GPU资源。

目前流行的一些布局是一把双刃剑,带给我们漂亮视觉感受的同时,也造成过度绘制的问题。为了最大限度提高应用的性能,我们必须尽量减少过度绘制。

幸运的是,我们可以通过手机设置里面的开发者选项,打开Show GPU Overdraw的选项,可以观察UI上的Overdraw情况。

蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。(当然最完美的情况是显示应用本来的颜色,不过正如前面提到的现在流行的一些布局为用户提供漂亮的视觉感受,所以某些Overdraw在某种程度上是可以接受的)。

避免Overdraw

有两种避免Overdraw的方法:

一、从视图中清除那些不必要的背景和图片,它们不会在最终渲染图像中显示。

可以从以下方面着手:

1、如果在布局中有指定背景,那么将Activity的背景设置为null。

getWindow().setBackgroundDrawable(null);

2、分析布局,将layout XML中一些不必要的背景属性去掉。

二、对视图中重叠的屏幕区域进行ClipRect定义,从而降低CPU和GPU的消耗。

Android系统利用了剪辑clipping技术来避免Overdraw,如果能确定某个对象会被完全阻挡,那就完全没必要绘制它。这是最重要的性能优化方法之一。对于复杂的自定义view,系统无法检查onDraw方法具体会执行什么操作,这些情况下,底层系统无法识别如何去绘制对象。从而有可能会造成多次绘制。

比如上图的view,只有最上面的牌是可见的,其他牌都被挡住了。这就意味着绘制那些重叠的像素就是浪费资源。

对于这个问题,我们可以使用Canvas类中一些特别的方法帮助Android系统识别被遮挡的不需要绘制的部分:

1、Canvas.clipRect - 指定特定的绘制边界,边界之外的不进行任何绘制。

如果我们知道自定义View可见部分的范围或者遮挡部分的范围,那么我们就可以定义ClipRect边界,避免遮挡区域的任何绘制操作。

PS:在使用Canvas.clipRect()的前后记得要调用Canvas.save()和Canvas.restore()。

2、Canvas.quickReject - 判断给定区域是否完全在剪辑矩形之外。从而跳过那些非矩形区域内的绘制操作。

Layouts, Invalidation And Performance

通常来说,Android需要把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息(包含GPU要绘制的全部对象的信息列表、执行绘制操作的OpenGL命令列表)。

在某个View第一次需要被渲染时,DisplayList会因此而被创建,当这个View要显示到屏幕上时,我们会执行GPU的绘制指令来进行渲染。如果你在后续有执行类似移动这个View的位置等操作而需要再次渲染这个View时,我们就仅仅需要额外操作一次渲染指令就够了。然而如果你修改了View中的某些可见组件,那么之前的DisplayList就无法继续使用了,我们需要回头重新创建一个DisplayList并且重新执行渲染指令并更新到屏幕上。

需要注意的是:任何时候View中的绘制内容发生变化时,都会重新执行创建DisplayList,渲染DisplayList,更新到屏幕上等一系列操作。这个流程的表现性能取决于你的View的复杂程度,View的状态变化以及渲染管道的执行性能。举个例子,假设某个Button的大小需要增大到目前的两倍,在增大Button大小之前,需要通过父View重新计算并摆放其他子View的位置。修改View的大小会触发整个ViewHierarcy的重新计算大小的操作。如果是修改View的位置则会触发ViewHierarch重新计算其他View的位置。如果布局很复杂,这就会很容易导致严重的性能问题。

Hierarchy Viewer可以帮助我们快速可视化整个UI结构,并让我们理解这个结构内的独特视图的相对渲染性能。

布局分析工具 - Hierarchy Viewer

Hierarchy Viewer可以帮助我们快速可视化整个UI结构,并让我们理解这个结构内的独特视图的相对渲染性能。我们可以通过它来查看布局、使得布局尽量简单化、扁平化、移除非必要的布局;同时对渲染相对比较慢的view进行分析,减少Measure、Layout的计算时间。

具体来说,

1、分析页面布局结构

查看页面布局

2、分析渲染性能

点击“Venn”按钮(可以多点击几次让资源达到平衡,结果更准确)查看记录每个节点rendering pipeline的过程,每个节点的三个圆点从左到右依次是measure、layout、draw的过程。圆点的颜色表示这个节点相对于所以其他已经概要显示的节点的性能,分别有绿色、黄色、红色。绿色代表执行速度快于至少一半以上的其他视图节点,黄色表示执行速度属于比较慢的50%,红色意味着这是视图层级中最慢的节点。

对于红色节点,我们需要分析它可能存在的问题。显示红色的节点是否和理论上是最慢的节点是否一致。有必要说明的是,这里显示的是相对性能,可能它的绝对性能并不慢,所以我们还需要结合实际数字进行分析。

具体教程可以参考:

Device Setup for Hierarchy Viewer: Device Setup

Hierarchy Viewer Walkthrough

Profiling with Hierarchy Viewer

布局优化

1、尽量扁平化布局。

如果用RelativeLayout和LinearLayout实现一个相同的布局,通过Hierarchy Viewer可以分析得出RelativeLayout的性能是高于LinearLayout的。因为RelativeLayout更加地扁平化。

2、去掉多余layout节点。

我们知道,布局的渲染速度是跟节点数呈正比的,所以我们可以分析布局尝试去掉多余的节点。也可以利用如<merge>等标签来减少我们布局的层次。

总结

本文主要介绍了渲染相关的性能优化。分别介绍了Android系统渲染的底层机制和最主要的两个组件GPU&CPU。

从GPU的角度来说,存在的性能问题是过度绘制Overdraw,可以利用Show GPU Overdraw来分析,解决方案是修改Layout XML,去掉多余或重复的背景。如果是自定义View,可以利用Canvas的ClipRect来限制绘制区域。

从CPU的角度来说,存在的性能问题是布局渲染的效率,可以利用Hierarchy Views工具分析布局和性能,解决方案是修改Layout XML,使得布局层级减少,尽量扁平化。

转载于:https://my.oschina.net/u/3026396/blog/817305

Android性能优化-Render篇相关推荐

  1. android使用桢布局,Android性能优化UI篇

    在说性能优化之前,我们必须了解为什么要做性能优化,首先第一点肯定是为了用户体验,你想啊要是你的App用起来很卡,页面加载慢腾腾的,用户能一直忍受你吗?其二性能优化也是对我们程序员自己的考验,优化过程需 ...

  2. Android性能优化系列篇(三):崩溃优化+卡顿优化

    前言 汇总了一下众多大佬的性能优化文章,知识点,主要包含: UI优化/启动优化/崩溃优化/卡顿优化/安全性优化/弱网优化/APP深度优化等等等~ 本篇是第三篇:崩溃优化+卡顿优化 [非商业用途,如有侵 ...

  3. android性能优化透明,Android性能优化-图片篇

    (1)drawable目录详解(mdpi,hdpi,xhdpi,xxhdpi,xxxhdpi) 1.1.图片在各个目录中要如何存放?(必须理解) android的drawable目录有: drawab ...

  4. Android性能优化系列篇(五):弱网优化

    六.弱网优化 1.Serializable原理 通常我们使用Java的序列化与反序列化时,只需要将类实现Serializable接口即可,剩下的事情就交给了jdk.今天我们就来探究一下,Java序列化 ...

  5. Android性能优化(二)—— 内存优化

    在Android系统中,垃圾回收是自动的,比较隐蔽,这就导致一些内存问题表现的并不明显,出现问题后难以定位.常见的内存问题有内存泄漏.内存溢出(Out of Memory).内存抖动等. 我们做内存优 ...

  6. 全面解析Android性能优化,含腾讯、阿里、百度、京东、美团、爱奇艺等大厂实战解析

    前言 安卓开发大军浩浩荡荡,经过十多年的发展.红利期已过,现在是增量有限,存量厮杀,从争夺用户到争夺时长.不管是用户也好.企业也好,都对 App 的用户体验和性能提出了更高的要求. 如果你已经有 2 ...

  7. Android性能优化系列总篇

    目前性能优化专题已完成以下部分: 性能优化总纲--性能问题及性能调优方式 性能优化第四篇--移动网络优化 性能优化第三篇--Java(Android)代码优化 性能优化第二篇--布局优化 性能优化第一 ...

  8. Android 性能优化(62)---存检测、卡顿优化、耗电优化、APK瘦身——详解篇

    Android 性能优化,内存检测.卡顿优化.耗电优化.APK瘦身--详解篇 自2008年智能时代开始,Android操作系统一路高歌,10年智能机发展之路,如今 Android 9.0 代号P  都 ...

  9. Android 性能优化之String篇

    Android 性能优化之 String篇 关于String相关知识都是老掉牙的东西了,但我们经常可能在不经意的String 字符串拼接的情况下浪费内存,影响性能,也常常会成为触发内存OOM的最后一步 ...

最新文章

  1. R语言match函数对象之间的匹配实战
  2. 【译】SQL Server误区30日谈-Day8-有关对索引进行在线操作的误区
  3. minecraft服务器_如何使用Minecraft领域设置简单的无压力Minecraft服务器
  4. java和node.js 2018_node.js在2018年能继续火起来吗?我们来看看node.js的待遇情况
  5. Wrapper+map实现页面显示
  6. 推荐系统遇上深度学习(五)--DeepCross Network模型理论和实践
  7. idea可以使用flash框架吗_可以使用 C# 的 Web 前端框架 Blazor
  8. java xfire指定参数名_XFire绑定Aegis自定义友好参数(复杂对象)
  9. 安装其他插件svn消失
  10. Qt 判断文件或文件夹是否存在及创建文件夹
  11. 邢台市初中计算机考试,2019年邢台中考总分多少分,邢台中考各个科目多少分
  12. python-os库函数一些用法记录
  13. 小说APP网站源码运营版+在线采集
  14. Tushare库之日线行情
  15. 扒一扒:2020台湾Android-Kotlin--Java-面試題庫,竟如此--
  16. css鼠标划出动画(transition属性详解)
  17. 基本数据类型有哪些?
  18. mysql启动失败 查看日志文件_mysql诊断启动问题、查看日志文件详解
  19. 知行合一拿什么保证?
  20. python---打印函数print延时且不按顺序输出

热门文章

  1. 如何进行需求矩阵管理
  2. SetWindowPos详解
  3. MFC中 CArray(template)的应用
  4. 大数据_Flink_数据处理_案例WorldCount_批处理版---Flink工作笔记0008
  5. 技术研究院005---如何基于DDD构建微服务架构
  6. java零碎要点---class()、getClass()、Class.forName()、getClassLoader()的区别和联系--又不详细的地方求补充
  7. Jonit Bayesian 的推导
  8. 手把手教你安装 FastAdmin 到虚拟主机 (phpStudy)
  9. php界面框架luy_LazyPHP
  10. python 组合数据_python-组合数据类