演讲实录——Android性能优化指南 [复制链接]

2019-4-26 10:24
九霄逆鳞 阅读:727 评论:0 赞:0
Tag:  

本文来自2019安卓巴士开发者大会现场实录,由于录入匆忙,内容可能存在偏差,欢迎大家扫描文末二维码查看现场实录视频和下载大会完整PPT。作者:邱炬


风雨再大不过是学习路上的点缀,雷声再响也阻止不了开发者渴望成长的步伐!

——美图高级安卓开发工程师  邱炬


各位开发者大家下午好,我今天给大家演讲的题目是《Android性能优化指南》。
其实这个主题特别大,花1、2天讲解都讲不完,我今天概括的讲一下。屏幕上看我们有90多个PPT,所以可能我的语速会比较快,大家想要拍照的话手速就要快一点。
我主要从四个方面来演讲,第一个是为什么需要性能优化,第二个是什么是性能优化,第三个性能优化应该怎么做,第四个是一些注意事项。
现在我先说一下为什么要做性能优化,这个非常简单,我们都是给产品增加功能,这个时候就需要APP运行,我们设备商的迭代就非常重要。我们发现几年之前的手机运行和现在的手机差得比较多,我们的软件不断的上升,功能在不断的迭代,所以对于硬件的需求更大。还有我们的安卓碎片化也非常严重,同时我们想要有一些人口福利的话,这个本身也是我们需要优化的一个过程,轻微的问题就是延迟,严重的就是无法响应,所以我要说的第一个性能优化是可以提升用户的存留的。

我这里说两个例子,Pinterest他们做了一个调研,他们性能优化了之后用户的注册率增加了40%,他们做的一个缩短时间的简单改进用户数就有了一个很大的提升。还有一个是BBC他们发现网站的加载时间每增加1秒就会失去一些用户,所以他们也做了性能优化。
所以我们说产品的目的是让用户与我们的内容进行互动,也就是消费数据,所以我们说的第二个点就是提升用户的转化率。简单来说就是商业化,你有了用户是首先,有了用户之后我们说一个例子,就是Mobify,他们首页加载的施加每减少100毫秒,他们发现基于会话的转化率增加1.11%,年均收入都有一个比较大的增长。
还有一个谷歌的分析平台,就是你的网页的加载时间在5秒之内可以完成这个收益是非常显著的,留住用户是第一个,转化率是第二个,第三个就是提升用户的体验。这里我们我也说一个例子,这个例子当中我们的右边有一个用户的简单信息界面,我们想要加载数据的话,有两个地方可以优化,一个是策略型,一个是效率型。第一个策略型就是增加用户的主观感受,它退出你这个页面的可能性就减少了。第三个是效率型,我们加载一个图片的时候,我们把一个大图换成一个小图片,这个也是一个优化。

所以我们总结一下为什么要性能优化,就是吸引用户提升用户的转化率赚更多的钱,实际开发当中我们要注意用户的主观感受和实际体验,自然而然产品的价值就上升了。
还有第二个点什么是性能优化,这里我总结一下就是增加我们的负载能力,从而提升我们的性能让程序更快的运行。这个地方有四个点,这四个点都是和用户息息相关的,用户做一个操作的时候,我们希望用户是100毫秒完成,并且动画要流畅,我们设置动画或滚动时,在10毫秒以内生成,还有最大程度增加主线程的空闲时间,并且持续的吸引用户,在1000毫秒以内呈现交互内容。
这个是一个延迟和用户反映之间的简单的图,大家可以看一下,比较关注的一个是16毫,这个是我们动画的点,100毫秒以下是我们的用户响应的操作,就是用户打游戏的时候不应该大于100毫秒,否则用户的体验就会非常差。100—300用户就可以感受到延迟了,再多到了1秒的话用户的注意力会转移,10秒以上用户就会抛弃你。我用过一些政府的软件,他们的验证码我点了一下获取,结果这个验证码是需要等一分钟的,我已经收到了验证码,这个时候我要等一分钟,这个时候的体验就非常不好。

还有一个我们的性能优化是需要有一定的指标的,首先要提出问题,找到问题,并且对问题进行分类。第二个我们修改之前要测量系统上面的问题,也就是找到我们的瓶颈,之后我们修复我们的瓶颈,修复好了之后我们做二次的测量,确定我们的修复是不是有效的。这个是一个大家需要的过程,其实开发过程当中我建议大家一定按照这样的流程做一做,有这样的数据支撑之后一定会更好。
第三个我们说一下性能优化怎么做,我说到这个题的时候大家有没有一些标签,比如你脑子里面有没有线程或者GPU这些关键词,有的话还是有一定的方向的,如果没有的话我们往下看。我们看性能优化就是把用户的哭脸变成笑脸,这个过程当中我们就要找到用户的痛点,最关键的就是卡顿不跟手,我们说BUG这个东西开发当中就安卓直接解决要,我们说到卡顿和不跟手,最主要的是用户基础上的设备信息,所以我们说要发挥设备上的一些东西,这些地方都可以进行优化。但是优化不是说就是把一个地方降低,不是我降低内存和CPU就是降低了,这个就是一个性能优化,所以说它是一个均衡,不是一个简简单单的降低就好了。
性能优化有5个方向,我们说到最后一个是电池方向,这个部分我们不做讲解,因为关于网络部分也给大家说一下,我们主要讲前面的显然还有计算以及内存方面的优化。

网络的这里很简单,里要配合服务器来改,因为一些东西和你的业务信息息息相关。
之后我们说到渲染,我们说屏幕关键的指标是1S内屏幕的刷新次数也就是每秒显示的帧数,我们看一下这个电影的摄像是记录的是一段时间的信息,而不是一瞬。我们说快门是有一个快门速度的,你打开的时间长一点可能就是看到的是另外一个图片,再长一点就是另外一个图片,看见的是同一帧,但是用户感觉是在动,所以这个就是动态模糊。其实在我们的一些游戏上也有一些应用,这里并不是说我们打开的时间越长越好,打开时间太长可能就会出现这种流光。

旁边这里是一个游戏,我们可以看到两帧其实都是完全静态的,里面没有模糊的东西,你看见的模糊就是因为它的渲染不体好,所以这个草看不清楚,旁边这个人在运动的时候,除了人之外,这个人还算清晰,大部分电影当中人也是模糊的,电影也在发展,我们都想缩短这个时间拿到更多的信息。
这个模糊不是简单的高斯模糊,这个图片我们高斯模糊之后就是这样的,我们动态模糊之后要获取相关的信息,我们看这三个图片,中间这个图本来是动的,但是没有动,这个地方就是说静态的时候,你的界面没有东西,第二个动画就是交互的时候这个也是一个波动状态,这里并不是说一定要达到60才是一个关键点。
所以我们总结一下动态模糊可以在低FPS时帮助大脑处理运动信息,所以我们需要更高的FPS展示,游戏这个东西也说过了,FPS作为屏幕流畅度的参考就不是一个绝对值,更多的还有一些其他方面的指标来衡量。我们说一下进行屏幕绘制的时候干了一些什么,我们需要很多帧的合成,进行数字的更新操作之后我们再绘制,如果其中某一个帧的时候,你简单的构建的时候你花了24毫秒,不是说这个时候你立刻可以停下刷新,这个时候你还是要等,这里因为有一个垂直同步的存在,这个是因为我们屏幕本身有一个刷新率,我们取的时候应该有一个实际去取。所以这个就是所谓的丢帧,说了丢帧我们说你把一个东西想要绘制在屏幕上的时候,不是简单的画上去,屏幕其实本身来说是由很多的像素组成的,归根到底屏幕还是很多的颗粒组成的,并没有想象当中得那么平滑。

我们把这样的一个三角形会知道我们后面的方格的屏幕上,我们绘制之后其实是这样的,并不是我们一个简单的三角形,我们方格的密度小一点就是左边的这个,密度高一点就是右边这个。我们看到整个的过程就设计我们CPU完成整个过程的运算,要把矢量的图片换成像素的信息,这个就是我们说的栅格化。

我们要显示这个鲨鱼的图片的话,首先要有一个鲨鱼的轮廓,也就是我们的矢量图,之后我们要渲染,它下面的纹理就是我们的一些颜色,这里需要准备这两个东西,之后交给我们的东西做合成,之后把它会知道我们的屏幕上,这个时候CPU本身是可以来做的,并且可以降低我们的效率,这里还可以通讯,而且里面还帮助我们维持了缓存什么的。

前面说了CPU和GPU分别要干的事情,就拿CPU来说要做的就是输入,你输入的可能是你的对象,不管是什么对象都要有一个解析的过程,之后做测量和布局,这个东西是最终给GPU的东西,这个过程一个是页面深度,非常深的情况下测量的时候耗费的时间会比较多,还有一个点是我们有一些控件可能失效了,这个时候也会进行额外的收集。所以我们说这里还是要使用一些工具,这个东西其实也可以看到它的层级,只是无法说我们测量之后的时间,这个东西其实我们可以通过后面的CPU耗时来统计。
解决方法就是布局简单化,对于我们GPU来说,它做的事情就是我们说的栅格化,你做要栅格化有多个矢量图在一起的时候,就有一个问题出现了,我们找到这个问题之后通过我们手机上的开发者调试工具就可以知道他里面是不是过度的绘制,知道了之后我们进行优化。无非就是更改表格,另外把我们的自定义的控件合并的时候进行剪切,并且让他重复的位置避免重复。

这个东西右边的截图是我们Android6.0以上的截图,对于Android6.0来说它的信息会多一些,这里不过多讲解。并且这里我们也可以知道我们每一次的绘制里面做了这些操作都是可以看到的,再有这里就是我们的OVERDRAW,我们要做的就是一个转化,并且我们要通过代码把背景移除掉,再有就是你的一些以加载图片的时候会有这样的一个图片,之后你加载之后把这个图片移除掉,这个时候最简单的就是把最后面的去掉。

还有自定义控件当中进行一个检查,之后再进行一个剪切。这个是我们的一个按纽,在左面是一个简单的按纽,右边是这个按纽的纵向图,每一层里面都有背景,我们要做的就是把后面的移除,让我们的按纽绘制的时候减少层次。

这个是谷歌的一个图片,在你的自定义的卡片布局当中,上面遮挡下面的时候,会在一个区域绘制两次,所以这个时候需要我们自己完成。再说性能优化的第二个点就是计算部分,看一个简单的例子,我们点击屏幕上面的按纽,要想得到右面的这个效果,其实它首先要控件点击在屏幕上,这个是系统集成控制的,这个里面会经过一系列的事件,之后可以拿到屏幕输入的事件,之后把它派发出去传递到我们当前的机制,之后我们进行解析之后进行调度,调度之后我们还需要层层的分析找到控件,这个时候有很多的派发和操作,所以主线程是非常繁忙的,这里很荣幸就可以导致你这里卡顿的情况。
前面我说到如果你卡顿了5秒以上就会出现一个ANR,其实这个东西一般出现的有四个点,简单来说你进入一个界面的时候,你在界面上进行线程的操作的时候,这个时候你不用你的手指触摸的话,你的程序还可以运作,但是你触摸了之后这个屏幕就会出现一个ANR,出现这个问题其实有几个点。一个是计算耗时,还有一个是等待,还有一个是得不到CPU的资源。要解决的话一个是让主线程等待,还有就是主线程在等待的时候,我们看到主线程在等待什么东西,下面会有两个线程有两个思索。

计算耗时的排查方案有以下几个点,还有一个就是通过我们的事件打点,这个比较是傻瓜式的,但是对于我们的测量是比较有用的,它影响的是所有的方法的耗时,所以它是均等的,它的占比是不会变的,并不会影响太多。
我们看一下IOS这个里面的工具,主窗口有五个地方需要给大家讲讲,第一个地方就是我们的记录配置,新版里面有五个配置,一个是简单的,一个是默认的,它有随机的堆栈的信息,知道我们当前的线程在干什么事情之后进行分析,这里有一个问题,就是当他这次取代到下次取代这个间隔有一个线程进入了堆栈,这个时候就不会捕获,这样我们的性能点分析的时候就会出现这样的一个方式,你的手机的负担就会比较大。

重点我们看一下第四个点,我们的CPU的核心数正在干什么,第三个点是我们的一些事件,你可以通过第一个点快速的选择,第四个点就是我们CPU的时间线,第五个点就是我们当前线程的活跃点,如果出现了一个色块,那么活跃这里也有几个颜色,不同的颜色也有不同的活跃状态。
如果我们选择一个地方点击第二个地方录制之后,你看到的界面就是类似这样的,这样的东西当中,我们主要关注的也就这些地方,第一个是时间范围,这里你可以进行时间选择,你得到的分析就更加精确,第二个点是我们的时间戳,第三个是跟踪窗格,第四个是分析类型可以进行切换,后面有我们的调用图表还有我们的火焰表,第五个是我们的函数时间类型,这里有两个,一个是我们的真实方法耗时,还有一个CPU对线程的耗时,最大的差异是等待的话,这个线程没有进行资源利用,你选择的时候这个分析数据其实是没有的。

看一下我们这个简单的例子,这个里面启动了一个线程,里面做了一个简单的等待,之后做了一个非常大数据的数据打印,这个东西在我们系统跑下来是什么样?我们跑下来之后可以看到这样的一个效果,我有两个红框的部分,这个部分是真实的事件消耗的地方,之所以我们做CPU的消耗是因为我们点击屏幕的时候是有系统的进程通知的,所以说这个线程本身会占用一定的时间,还有我们主线程把基本上的控件进行刷新也会耗费一定的时间。
还有我们说的这个方法里面大家可以知道我们当前在这个点这个地方CPU正在消耗的时候我们正在干什么事情,如果我们切换到我们下面这个线程的时候,我们再把它现在到我们线程实际消耗的地方,我们可以显现这里有非常多的消耗,我们刚刚说了旁边有时间的选择方式,如果我们选择第一种方式,那么就是这样的,如果我们选择第二个方式,我们就可以知道当前的线程没有做太多的内部计算的操作,其实它的大部分是都是在等待,所以它是没有统计信息的。

之后我们到第四个我们可以看到方法的耗时的排列,我们从多到少做一个排序,你点击进去这个方法的话,你要看子方法的耗时,通过这样我们就可以做一个性能优化。下面是几个不同的方式,我们所看到的内容是不一样的,给大家看一下这个Call Chart,你看到的这个图和下面的两个图,这里调用B方法的时候耗费的时间是比B方法少的,我们看D方法内部有C和B,C这边我们有一个倡议就是C和B加起来还没有把D占满,我们发现每一个方法耗费时间比较长的话我们会看到,但是有的耗费时间比较短,但是多次占用的话,我们把这个问题转换过去其实是有一定的关联的,简单来说就是把同样调度顺序的方法进行了汇总,这边来说我们的B方法是从A到D到B的,所以这里的调度顺序是一样的,我们可以合并起来作为总时间。所以我们说这个条形的柱状图自动的加长,我们的C1和C3也是一样的,这样的方式可以帮助我们直接排查,可以让我们知道哪个方法的综合调度比较好,这个过程当中我们可以看到B123耗时是比较长的,并且调用的子方法和消耗办法都是一样的。
我们这些C函数作为调度的时候都可以给予帮助,下次给大家讲一下这个。我说一下Trace  System  Calls这个方式,这个模式得到的数据比前面要多得多,一个是我们的帧,一个是我们的CPU,CPU我分析的时候,我发现我们前面可能没有做系统的调度,这里仅仅是从4到7的进行调度,我们看到我们选择的主线程分布到这4个CPU里面可以知道整个CPU里面的调度过程,这个时候多并发的过程我们可以知道CPU的情况。

选择不同的线程的时候,CPU其实是会变化的,它标识的是我们当前CPU的情况,这个东西是非常重要的,与此同时我们刚刚说的这个图我们选择主线程的时候,我们可以看到底部,这里最上面的大箭头是有一个54.8毫秒的现象,这个丢帧就是我们选择了这个区间的时候下面就选择了我们的线程,之后进行了输出,之后我们看到下面得做的一个事情就是当前CPU做了一个额外的操作,之后做了一个解析布局的操作,这里地方其实也是一个可以优化的点。
我们说一下常见的影响性能的操作,我们看到对于我们APP打下会有一个增加,比如你做了一些设计模式和策略模式,它这里可以提供很多的便利,这个情况下你也可以使用一下,但是不要太多。还有一个就是我们的StringBuilder替换为+运算符,其实这里可以做一个时间的简单测量。
还有一些比如你尽可能使用直接类型,避免使用BigInteger。这里说完了我们说一下内存优化,这里不说原理了,不说我们内存是怎么分配的,我们说一下影响内存的东西就是我们的GC,一个是我们的内存抖动,还有一个是我们的内存泄露,第一个内存抖动就是短时内大量的对象进出方法栈,还有一个内存修楼是不再需要的对象被意外持有,第三个影响就是大对象引起的GC操作。

我们看一下内存分析的东西也是一样,我们看这个模型来说这里有七个点,第一个点我们可以知道当前的内存里面的对象可以进行强制回收,可以回收的话就没有问题,不可以回收的话有没有内存泄露就取决于我们的实际情况。还有一个是我们的捕获的类型以及后面的456都没有太多的信息。
我们看一下我们的内存抖动,出现内存抖动的话,你看到的图其实和网络上的不一样,这个图不是峰值,但是出现了很多的金色的标记,有一个虚线一直往上,说明我们创造了非常多的对象,并且在不断的上升,而且我们的内存上没有一个太大的幅度,但是你频繁的回收就出现了内存的抖动。

排查这里我们拿到这个信息先选择抖动的区间,之后选择分配对象最多的点,再点一下第四个这里可能需要你点击不同的地方,我们一般是点第一个就好了,这里我们点第四个位置,点了之后我就可以看到当前对象引用的最小路径,我们看到我们的线程是这里被引用的,所以我们可以知道这个地方会有一个抖动的地方。
这个代码很简单,就是这个线程里面做循环,之后不断的做拼接,这个就是拼好了之后用完了就丢了,所以这里有一个内存的抖动,表现为一个大局的情况,我们的内存是有不同的很多小的波浪,我们的CPU也有很多不同的小的波浪,上面也有一些波浪的出现,这个是我们的方法里面做了一些耗时操作。

这里说一下内存泄露,表现的是内存不断的上涨,但是可能这里回收了一部分,但是没有所有都回收。对象的数量在不断的上涨,可以还原的地方就是内存的抖动,下面这里很多可以回收的标就存在,我们说最上面的箭头也有一个Java占用的内存,这个内存是不断的上升的话这个时候就有一个内存泄露的存在。
我们拿到一个区间之后进行排查,之后要多次操作你的界面,如果这个幅度平稳下来了,我们最好选择平稳的区间,你选择的上升的区间就不利于你的分析,如果你已经平稳了,你选择好了之后我们看第二个点。第三个点这里当前的对象数量有多少,还有我们选的后面的排序方式我们排序一下,当前占用的内存大小来确定当前的排序。

我们到了这个地方某个地方都是一样的,之后我们看一下我们引用的链,拿了之后就可以确定问题,这个是我们排查上面的一些简单步骤,其实我们这个案例是一个短时泄露,之后我们进行其实是可以内存泄露的,第三个位置这里我们也可以看到我们的一个总数和占有都得到了一个想象。
这个期间我们看一下总得概览,大概就是这样的一个情况,这个是我们刚刚的例子,也就是我们把创建的组件全部存起来,为什么说这个是一个短时内存泄露,我们当前的堆栈引起的话没有办法得到计算,但是线程的栈之后也可以得到回收,所以我们说内存泄露有短时的和永久的。
内存处理思路我们对于怀疑的地方进行压测,把多余的一些内存排除掉,有必要的情况下可以录制一段内存的记录。还有有必要的时候我们可以打出多份内存的快照。最后说一下我们性能优化的准则,其实第一个就是不要过早的优化你的程序,不是说你开发的过程当中不优化你的程序,你开发的过程当中有一些耗时可以及时优化,因为有可能你第三天的需求有可能把你前面的推翻。

第二个良好的结构是优于一切的,你APP这里是可以避免非常多的问题。第三个是采用高效的算法。第四个是使用工具找到性能瓶颈,切忌过度优化,有可能你优化的过度了,第二天就立刻被推翻了,第四点我建议大家深入的理解,我看似把性能优化了,但是可能带来了更多的隐患。
还有后续这里很多东西都没有给大家讲,我们前面讲的就是一个简单的指标,这个东西其实大家后面可以关注一下我们的文章之类的,我们也会进行一个推送,计算这里我们也有非常多的内容可以和大家讲解,大家也可以自己拓展。关于内存也是一样,还有我们的网络电池这里也是一样的,我们这里还有各种工具的对比,本来我这里也有很多的工具对比,我大概有20多种工具,这里的这些工具大家可以看一下,这里的工具我都没有放PPT,我是直接截图的,所以没有颜色。
后面我会把这些整理出来,希望大家喜欢,最后谢谢大家,感谢大家的聆听,谢谢。

作者Github: https://github.com/qiujuer 
慕课网讲师页介绍:https://www.imooc.com/t/1339513

现场PPT分享:关注【安卓巴士Android开发者门户】公众号,后台回复“420”获取讲师完整PPT。
大会现场视频小程序:



我来说两句
您需要登录后才可以评论 登录 | 立即注册
facelist
所有评论(0)
领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )