渲染离不开屏幕,Android中的屏幕碎片化比较严重,尺寸大小不一,材质也是屏幕重要的因素。 目前智能手机主流的屏幕可分为两大类即液晶显示器;
除了屏幕 UI渲染还要依赖另外两个核心的硬件: 「CPU」 和 「GPU」。
UI 组件在绘制到屏幕之前,都需要经过 Rasterization (栅格化)操作,而栅格化又是一个非常耗时的操作。GPU的引入就是为了加快栅格化
在没有 GPU 的时代,UI 的绘制任务完全由 CPU 完成,CPU 既要负责 UI 绘制还要负责内存管理、逻辑运算等其他任务,这就导致 CPU 的任务繁多,因此性能也会有影响. CPU与GPU 在结构设计上完全不同,如下图:
从上图可以看出,CPU 的控制器较为复杂,而 ALU 数量较少,因此 CPU 更擅长各种复杂的逻辑运算,但不擅长数学尤其是浮点运算 而 GPU 的设计正是为实现大量数学运算。GPU 的控制器比较简单,但包含大量 ALU。GPU 中的 ALU 使用了并行设计,且具有较多的浮点运算单元。「可以帮助我们加快栅格化操作」。
在处理3D场景时,通常Android使用OpenGL ES,不过在Android 7.0 添加了对 Vulkan 的支持
Vulkan 的设计目标是取代 OpenGL,Vulkan 是一个相当低级别的 API,并且提供并行的任务处理。Vulkan 还能够渲染 2D 图形应用程序。除了其较低的 CPU 使用率,Vulkan 还能够更好地在多个 CPU 内核之间分配工作。在功耗、多核优化提升绘图调用上有着非常明显的优势。
而且从性能考虑,「GPU是用户进程唯一可以直接操作的硬件」,是个特例
这与Android 「Client-Sever」 架构不同,一般情况下 应用与硬件交互都需要经过binder委托给系统服务
有了GPU,便可以在渲染中加速栅格化的过程,软件绘制往往是cpu借助2d渲染引起skia的能力,去完成渲染,一般实在低端机器
软件绘制使用 Skia 库,它是一款能在低端设备,如手机呈现高质量的 2D 跨平台图形框架,类似 Chrome、Flutter 内部使用的都是 Skia 库
有了上面知识之后,我们视角切入到Android系统中来,先来看下Android的图像系统组件 无论开发者使用什么渲染 API,一切内容都会渲染到 「Surface」 上。Surface 表示缓冲区队列中的生产方,而缓冲区队列通常会被 SurfaceFlinger 消耗。在 Android 平台上创建的每个窗口都由 Surface 提供支持。所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到屏幕。
下图显示了关键组件如何协同工作
我们可以看到 有关键的两个角色,
整个图形渲染系统就是采用了**「生产者消费者模式」**。屏幕渲染的核心,是对图像数据的生产和消费。 「生产和消费的对象,是BufferQueue 里的 Buffer」。
这是描述Buffer是如何流动起来的,是对上面流程的的数据化抽象
左侧的对象是生成图形缓冲区的渲染器,如主屏幕、状态栏和系统界面。SurfaceFlinger 是合成器,而硬件混合渲染器是混合渲染器。
「BufferQueues 是 Android 图形组件之间的粘合剂」。它们是一对队列,可以调解缓冲区从生产方到消耗方的固定周期。一旦生产方移交其缓冲区,SurfaceFlinger 便会负责将所有内容合成到显示部分。 抽象成生成消费者模型的 BufferQueue 通信过程
BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。
BufferQueue 是将缓冲区池与队列相结合的数据结构,它使用 Binder IPC 在进程之间传递缓冲区。
Vsync 有两种,Vsync-app 和 Vsync-sf,前者用于告诉 Choreographer,是时候协调 app 生产buffer了;后者用于告诉 SurfaceFlinger,是时候来消费buffer合成并显示到屏幕了
如果把应用程序图形渲染过程当做一次绘画过程,那么绘画过程中 Android 的各个图形组件的作用是:
使用画笔 Skia /OpenGL 将内容绘制到 Surface 上,
「绘制的过程中如果使用 Open GL 渲染,那便是硬件加速」,否则纯靠 CPU 绘制染栅格化的过程就叫软件绘制。对于硬件绘制,我们通过调用 OpenGL ES 接口利用 GPU 完成绘制。
对 BufferQueue 理解了的话,那么可以延伸思考一下:
要了解这两个问题,就要了解:
更多精彩内容关注公众号 「Android茶话会」
这在后面会有介绍
为了使AndroidUI更加的丝滑,渲染系统也是下足了功夫,在持续演进,
Android 2D 渲染管道支持硬件加速,也就是说,在 View 的画布上执行的所有绘制操作都会使用 GPU。启用硬件加速需要更多资源,因此应用会占用更多内存
由于现在Android4.4以下的手机基本已被淘汰,所以以后的版本默认都是开启了硬件加速。
再没有硬件加速之前主要是通过skia这种软件方式来渲染UI,如下图所示
整个流程如上图所示:
整个渲染流程看上去比较简单,但是正如前面所说,CPU 对于图形处理并不是那么高效,这个过程完全没有利用 GPU 的高性能。
Android3.0,支持硬件加速,需要手动打开,Android4.0就默认开启硬件加速了,开启硬件加速流程如下
硬件加速绘制最核心就是通过 GPU 完成 Graphic Buffer 的内容绘制。 在Android 5.0之前,Android应用程序的Main Thread不仅负责用户输入,同时也是一个OpenGL线程,也负责渲染UI。通过引进Render Thread,我们就可以将UI渲染工作从Main Thread释放出来,交由Render Thread来处理,从而也使得Main Thread可以更专注高效地处理用户输入,这样使得在提高UI绘制效率的同时,也使得UI具有更高的响应
此外硬件绘制还引入了一个 DisplayList 的概念,每个 View 内部都有一个DisplayList,当某个 View 需要重绘时,将它标记为 Dirty。当需要重绘时,仅仅只需要重绘一个 View 的 DisplayList,而不是像软件绘制那样需要向上递归。这样可以大大减少绘图的操作数量,因而提高了渲染效率
Google 在 2012 年的 I0 大会上宣布了 Project Butter 黄油计划,并且在Android 4.1 中正式开启了这个机制。 Project Butter 主要包含三个组成部分,
其中 VSYNC (Vertical Synchronization) 是理解 Project Butter 的核心
对于 Android 4.0 CPU 可能会因为在忙其他的事情,导致没来得及处理 UI 绘制,导致卡顿。 为了解决这个问题,Project Butter 引入了 VSYNC,「它类似于时钟中断,每收到 VSYNC 中断,CPU 会立即准备 Buffer 数据」,
由于大部分显示设备刷新频率都是 60 Hz (一秒刷新 60次) ,也就是说一数据的准备都要在 16ms 内完成
这样应用总是在 VSYNC 边界上开始绘制,而 SurfaceFlinger 总是在 VSYNC 边界上进行合成。这样可以消除卡顿,并提升图形的视觉表现。
在Android 4.1 之前,Android 使用双缓冲机制。
通常不同的 View 或者Activity 都会共用同一个 Window,也就是共用同一个 Surface。而每个 Surface 都会有一个 BufferQueue 缓存队列,但是这个队列会由 SurfaceFlinger 管理通过匿名共享内存机制与 App 应用层交互
从上图可以看出
但是同一时间我们可能会有多个 Surface。这里面可能是不同应用的 Surface,也可能是同一个应用里面类似SurfaceView 和 TextureView,它们都会有自己单独的 Surface。
如果只有两个Graphic Buffer 缓存区A和 B,如果 CPU/GPU 绘制过程较长,超过了一个 VSYNC 信号周期,因为缓冲区 B 中的数据还没有准备完成,所以只能继续展示A 缓冲区的内容,这样缓冲区A和 B 都分别被显示设备和 GPU 占用,CPU 无法准备下一的数据,如下所示
如果再提供一个缓冲区,CPU、GPU 和显示设备都能使用各自的缓冲区工作,互不影响。 简单来说,三缓冲机制就是在双缓冲机制的基础上增加了一个 Graphic Buffer 缓冲区,这样可以最大限度的利用空闲时间,
带来的坏处是多使用了一个 Graphic Buffer 所占用的内存。
我们看到,在第一次Jank内,CPU使用了第三块缓冲区,自己计算了C帧的数据,
假如此时没有三缓冲,那么cpu就只能再继续等下一个vsync信号,也就是在图中蓝色A块的地方,才能开始计算C帧数据,就又引发下一次卡顿。
我们看到,「通过引入三缓冲,虽然不能避免卡顿问题,但是却可以大幅优化卡顿问题」,尤其是**「避免连续卡顿」,但是,三缓冲也有缺点,就是「内存消耗多」**,所以系统并非一直开启三缓冲,要想真正解决问题,还需要在cpu层对数据尽量优化,从而减小cpu和gpu的计算量,
这个名字起的非常贴合且很文艺,舞蹈是有节奏的,节奏使舞蹈的每个动作更加协调和连贯; 视图刷新也是如此,Choreographer 可以接收系统的 VSYNC 信号
Choreographer 和 Vsync 共同解决生产者何时生产,消费者何时消费的问题
因此需要一个「协调者」来协调这个工作。 Android 这里的协调者就是 Choreographer——a person who composes the sequence of steps and moves for a performance of dance. Choreographer 协调生产者什么时候去生产——也就是什么时候去绘制一帧。既然要协调,那么肯定是需要有一个协调的依据,这个依据就是 Vsync 信号——也就是垂直同步信号
具体来说是 Vsync_app
增加检测过度绘制工具,开发者模式打开检查开关之后,可以通过不同颜色反应过度绘制的程度
最初的 Android 版本里面是没有渲染线程的,渲染工作都是在主线程完成,使用的也都是 CPU ,调用的是 libSkia 这个库, Android5.0之后,进一步提升渲染性能。所有的GL 命令执行都放到单独线程RenderThread(渲染线程)「中,仅与GPU对话,渲染线程在 RenderNode 中存有染顿的所有信息,可以做些属性动画,这样即便主线程有耗时操作的时候也可以保证动画流程。 是当UI开启硬件加速后,用于分担主线程绘制任务的渲染线程,主线程的 draw 函数并没有真正的执行 drawCall ,而是把要 draw 的内容记录到」** DIsplayList 里面,同步到 RenderThread **中,一旦同步完成,主线程就可以被释放出来做其他的事情,RenderThread 则继续进行渲染工作
GL的替代品
NativeAllocationRegistry是Android 8.0引入的一种辅助自动回收native内存的一种机制,当Java对象因为GC被回收后,NativeAllocationRegistry可以辅助回收Java对象所申请的native内存
Android 性能优化篇:https://qr18.cn/FVlo89
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap