【转】本文发布于微信公众号 - QQ音乐技术团队 - 腾讯Bugly(http://bugly.qq.com
终端的开发,首当其冲的就是视图、动画的渲染,切换等等。用户使用 App 时最直接的体验就是这个界面好不好看,动画炫不炫,滑动流不流畅。UI就是 App 的门面,它的体验伴随着用户使用 App 的整个过程。如果UI失败,用户是不会有打开第二次的欲望的。
iOS 为开发者提供了丰富的 Framework(UIKit,Core Animation,Core Graphic,OpenGL 等等)来满足开发从上层到底层各种各样的需求。不得不说苹果很牛逼,很多接口你根本不需要理解背后的原理就能上手使用并且满足你大部分的需求,但是,如果遇到性能问题就容易抓瞎。易用性跟优化就是个矛盾体,就像 ARC 一样,当你没有遇到内存问题的时候用得很爽,一旦遇到了,就要要求你比在用 MRC 的时候更加了解 iOS 的内存机制。UI 亦是如此。况且,作为鹅厂的员工当然不能仅限于知道怎么用。我们要知其然还要知其所以然。好了,废话不说,我们进入主题:看看 iOS 是如何渲染视图和动画的,以及在我们遇到渲染的性能问题时怎么做优化。
(注意:以下内容是笔者的一些踩坑经验和总结, 欢迎探讨!)
先来看看官方对 Core Animation 的一段说明:
可以看出iOS渲染视图的核心是 Core Animation。从底层到上层依此是 GPU->(OpenGL、Core Graphic) -> Core Animation -> UIKit。
在 iOS上,动画和视图的渲染其实是在另外一个进程做的(下面我们叫这个进程 render server),在 iOS 5 以前这个进程叫 SpringBoard,在 iOS 6 之后叫 BackBoard。
下面这幅图是使用项目录制视频的时候(大量视图渲染),整个系统的进程情况:
可以很清楚地看到 BackBoard 这个进程的情况。
在 APP 内部的有4个阶段:
在 APP 外部的2个阶段:
当这些数据到达 render server 后,会被反序列化成 render tree。然后 render server 会做下面的两件事:
如果做动画的话,最后的两个步骤会一直重复直到动画结束。
我们都知道 iOS 设备的屏幕刷新频率是 60Hz。如果上面的这些步骤在一个刷新周期之内无法做完(1/60s),就会造成掉帧。
我们看看有哪些操作可能会过度消耗 CPU 或者 GPU,从而造成掉帧。
隐藏的绘制:catextlayer 和 uilabel 都是将 text 画入 backing image 的。如果改了一个包含 text 的 view 的 frame 的话,text 会被重新绘制。
Rasterize:当使用 layer 的 shouldRasterize 的时候(记得设置适当的 laye r的 rasterizationScale),layer 会被强制绘制到一个 offscreen image 上,并且会被缓存起来。这种方法可以用来缓存绘制耗时(比如有比较绚的效果)但是不经常改的 layer,如果 layer 经常变,就不适合用。
离屏绘制: 使用 Rounded corner, layer masks, drop shadows 的效果可以使用 stretchable images。比如实现 rounded corner,可以将一个圆形的图片赋值于 layer 的 content 的属性。并且设置好 contentsCenter 和 contentScale 属性。
Blending and Overdraw :如果一个 layer 被另一个 layer 完全遮盖,GPU 会做优化不渲染被遮盖的 layer,但是计算一个 layer 是否被另一个 layer 完全遮盖是很耗 cpu 的。将几个半透明的 layer 的 color 融合在一起也是很消耗的。
我们要做的:
这样会减少 blending 和 overdraw。
如果使用 image 的话,尽量避免设置 image 的 alpha 为透明的,如果一些效果需要几个图片融合而成,就让设计用一张图画好,不要让程序在运行的时候去动态的融合。
好了,介绍完这些渲染优化需要注意的点,让我们用 instrument 的 Core Animation 和 GPU Driver 来看看一些具体的例子。
Core Animation:
这下这两幅图是测量项目详情页的半透明的 layer 的情况。可以看到详情页这里半透明的 layer 还是比较多的,但不是说半透明的 layer 很多,范围很大就要优化,要参看 GPU Driver 的测量情况看,下面会介绍
Color Hits Green and Misses Red:当使用shouldRasterize的时候,layer drawing 会被缓存起来,如果 rasterized 的 layer 需要被重新绘制,会标示红。
下面是项目消息列表的 cell 设置 shouldRasterize=YES 的情况,每个 cell 的 layer 的 backing image 会被缓存起来,如果往下滚动 tableview 的话,由于 cell 会被复用,这样 layer 就会被重绘,会被标红。
Color Offscreen-Rendered Yellow:如果要做 offscreen drawing 的话,会标成黄色。
下面是项目的首页,由于圆形头像的实现方式是设置 roundedCorner 的属性来实现,所以会触发 offscreen drawing。
Core Animation Template 只是能让开发者直观地看到哪些地方有可能需要优化,但是到底要不要优化,还是要看 GPU Driver 的表现。
GPU Driver
Renderer Utilization ——如果这个值大于50%的话,表示 GPU 的性能受到 fill-rate 的限制,可能有太多的 Offscreen rendering,overdraw,blending。
Tiler Utilization ——如果这个值大于50%,表示可能有太多的 layers。
我们以上面的那个项目的详情页为例,看看 GPU driver 的测量:
可以看到这 Renderer Utilization 是20%左右,Tiler Utilization 时15%,可以不用优化,(当然这里我们先不考虑 CPU 的使用情况,只是单单针对上面 Core Animation Template 的一些测量说不用做针对性的优化)
这里想说的一点是,以上所说的点只是在我们遇到渲染性能问题的时候給我们提供优化的方向跟思路。优化往往代表着更复杂,难懂的代码,在没有遇到渲染性能问题的时候不要过度优化。