前言
本文将介绍 Android 的渲染机制。主要是带着大家了解 Android 是如何绘制并显示一帧图像的,同时会涉及 Skia底层渲染的使用,以及关联到flutter的UI体系设计,感兴趣的可以在阅读的同时使用 Skia自己搭建一套flutter UI体系 进行尝试。在大厂里渲染机制一直都是绕不开的话题,不管是工作中、还是面试中。
例如:关于渲染所涉及到的的相关问题:
1.让你聊一聊渲染机制?而你答案往往是在讲绘制流程?
2.被问到VSYNC与Choreographer,完全不清楚到底该回答哪一块,而选择回答基础概念?
3.在工作中出现了卡顿问题,但是自己不知从何下手去处理?
4.碰到Flutter相关显示问题而不知道怎么处理?
…
渲染机制到底是什么?
1.Google 对于渲染机制的设计考量
有很多初级开发者会把渲染机制与android的绘制流程当中的测量、布局、绘制这个流程联系起来,其实不然,Android的渲染机制的本质目的是去探索如何将自己写的一个View显示在屏幕上的整个过程,这是一个View从 文本数据(layout xml)转换到实例数据(View 对象)然后再转换成图像数据(由底层调用Skia 利用GPU生成的Bitmap数据)后推送给屏幕显示的过程,具体流程大致如下图所示:
1.1.在了解完上面过程之后我们首先要明白屏幕的数据诉求是什么?
屏幕是一组硬件,这个组件需要展示的数据实际上是一组图形数据,也就是屏幕的数据诉求其实是一组bitmap数据。
1.2.View如何转换成一组Bitmap数据的?
这个从结果上探索其实很简单,大家都做过自定义View,在自定义View时,所有的图像生产都是由onDraw当中的Canvas主导的,如果你深入源码去看会发现当前Canvas的所有drawXXX函数都是在调用native层的对应函数,利用Skia图像引擎进行数据生产,具体详细过程较为繁琐,下图为其中之一个案例
1.3.surfaceflinger与Choreographer的出现到底是为了解决什么问题?
首先从设计层面上来讲要考虑一个问题,为什么google会要专门设计一个进程来管理屏幕显示问题,而不是直接找屏幕驱动去完成图像数据的录入?因为直接走屏幕驱动必然会面对屏幕硬件性能(刷新率)与当前APP的CPU/GPU制图速度(FPS)所带来的两个问题,屏幕撕裂与跳帧。
google在设计考量中去中和这两个硬件的性能速度,采取的是将两者的绘制速度与频率控制在一定的范围内,也就是我们常提到的60FPS这个阈值上,所以他需要对于屏幕的刷新评率与当前的APP制图速度进行控制,所以他设计了surfaceflinger对于刷新率进行控制,在APP的绘制流程中采用了Choreographer对于制图速度进行控制。
至于控制的策略不外乎就是依赖于一个时间将1s 切割成60份,而这个时间依据就是我们看到的所谓VSYNC信号,VSYNC本质上是驱动提供出来的上一次绘图时间,然后经过surfaceflinger的管理协调,以及同步给APP的Choreographer进行对应的管理,具体结构如下图所示:
1.4.Bitmap数据的绘制保存点与走向
在上图中我们也能够看到在APP与surfaceflinger之间的交过过程中图像数据是采取类似生产消费模式进行沟通的,因为底层是用C的Skia图像渲染引擎,Skia能实现用户手指交互与渲染、文字排版引擎等。在安卓上,系统自带了Skia,在每个平台上都有包含Skia引擎的, 通过Skia引擎生产的相关数据, 会给如到一个固定位置,有surfaceflinger方进行消费获取,具体流程如下图所示
1.5.渲染机制与卡顿的联系是什么?
很多小伙伴对于优化内容没有一个明确的认知,性能优化看上去非常的高大上,但其实就是“细节决定成败”的概念。需要对原理性的东西了解清楚,每一步都有什么不一样,针对每个步骤进行细致化的优化。性能优化是一种思想,而不是一套具体的操作方法。
其主要的套路遵循三个原则
卡顿的痛点有多时候在于大家找不到问题所有,所以前面的渲染机制的支撑就非常关键,透过渲染能够得到卡顿本质出现的问题,具体如下图所示:
还有很多同学是否也有遇到过拿到工具不知道如何去用?这里给大家提供一个思路,一般用三套工具能够你在工作中的所有问题:
1.systrace 能够快速定位事故类型
2.Choreographer能够快速监听帧率变化
3.透过Looper机制能够快速定位代码堆栈信息
2.Flutter 3.0 下UI绘制体系的构建
其实如果大家能站在google的角度去看懂android渲染体系的设计,那么flutter的UI体系构建在大家眼里就不在是一个什么秘密,具体Flutter3.0 框架实现原理如下图所示,跨平台的依据就是在底层的Skia库上面
Flutter3.0 提供了一套Dart API,在底层通过OpenGL这种跨平台的绘制库,也就是下图的GPU部分(内部会调用操作系统API)实现了一套代码跨多端。由于Dart API 也是调用操作系统API,所以它的性能接近原生。虽然Dart是先调用了OpenGL,OpenGL才会调用操作系统API,但是这仍然是原生渲染,因为OpenGL只是操作系统API的一个封装库,它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器,所以不会有性能损失
对于上图来说,需要理解大量framework源码,你能找到核心代码吗?你能找到合理的优化位置吗?图中相关Framework源码解析文档给大家分享一份,如下图(部分资料整理自互联网):
作为过来人,发现很多学习者都在渲染的面试和实战上面临着很多的困扰,比如:
- 工作场景中遇到“卡顿”难题,往往只能靠盲猜和感觉,用临时性的补救措施去掩盖,看似解决了问题,但下次同样的问题又会发作,原因则是缺乏方法论、架构思维的指引以及工具支持;
- 能力修炼中,缺乏互联网项目这一实践环境,对“性能优化”只能通过理论知识进行想象,无法认识其在工作实战中的真实面目和实操过程;
- 职场晋升中,只管功能开发,不了解组件设计原理,缺少深入地思考与总结,无法完成高并发、高性能系统设计这类高阶工作,难以在工作中大展拳脚,而有挑战的工作往往留给有准备的人。
总之,一旦遇到性能问题,很少人能够由点及面逆向分析,最终找到瓶颈点和优化方法,可见性能优化需要对知识进行综合的掌握与灵活的运用,属于安卓知识领域的的深水区,也是衡量一个研发能力高低的标准之一。
下面是我在学习中整理的一些学习资料,大家如不嫌弃可以点击【Android 八大知识体】地址查看获取