Android 渲染机制——原理篇(显示原理全过程解析)

帧率


每秒的帧数(fps)或者说帧率是以帧为单位的位图图像每秒连续出现在显示器上的次数(速率)。简单来说就是一秒钟,屏幕显示多少张画面。

为什么是 60fps ?

人类视觉的时间敏感度和分辨率根据视觉刺激的类型和特征而变化,并且在个体之间不同。人类视觉系统每秒可处理10到12个图像并单独感知它们,而较高的速率则被视为运动。12fps 大概类似手动快速翻动书籍的帧率,这明显是可以感知到不够顺滑的。24fps 使得人眼感知的是连续线性的运动,这其实是归功于运动模糊的效果。24fps 是电影胶圈通常使用的帧率,因为这个帧率已经足够支撑大部分电影画面需要表达的内容,同时能够最大的减少费用支出。一般来说 30fps 就是可以接受的,低于 30fps 是无法顺畅表现绚丽的画面内容的,此时将帧率提升至 60fps 则可以明显提升交互感和逼真感,但是一般来说超过 75fps 一般就不容易察觉到有明显的流畅度提升了,所以,超过 60fps 是没有必要的,并且如果帧率超过屏幕刷新率(手机屏幕通常都是 60fps 的刷新率)只会浪费图形处理的能力,因为屏幕不能以这么快的速度更新,这样超过刷新率的帧率就浪费掉了。

Android 在设计的时候,把帧频限定在了每秒 60 帧,当我们的 APP 的帧频 60fps 时,画面就会非常的流畅。但是通常由于各种各样的原因,帧频很可能会小于 60fps,这样就会出现丢帧现象,用户端表现为可感知的卡顿等现象。那面我们的帧频可以高于 60fps 吗,答案是否定的,这是因为界面刷新渲染依赖底层的 VSYNC 信号,VSYNC 信号以每秒 60 次的频率发送给上层,并且高于 60fps 的帧频也是没有必要的,因为人眼与大脑之间的协作无法感知超过 60fps 的画面更新。(大多数手机的屏幕刷新频率是 60hz,所以太高也没有意义)

开发 app 的性能目标就是保持 60fps,这意味着每一帧你只有 1000/60=16.67ms 的时间来处理所有的任务。如果 16ms 内没有办法把这一帧的任务执行完毕,就会发生丢帧的现象。丢帧越多,用户感受到的卡顿情况就越严重。

在这里插入图片描述

帧率与性能

大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能,也就是 16ms 内没有办法把这一帧的任务执行完毕,发生了丢帧的现象。从设计师和产品的角度,他们希望 App 能够有更多的动画、图片等时尚元素来实现流畅的用户体验。但是 Android 系统很有可能无法及时完成那些复杂的界面渲染操作。Android 系统每隔 16ms 发出 VSYNC 信号,触发对 UI 进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的 60fps,为了能够实现 60fps,这意味着程序的大多数操作都必须在 16ms 内完成。

在这里插入图片描述

如果 App 的某个操作花费时间是 24ms,系统在得到 VSYNC 信号的时候就无法进行正常渲染,这样就发生了丢帧现象。那么用户在 32ms 内看到的会是同一帧画面。

在这里插入图片描述

通常,越复杂的 UI,越容易导致卡顿,例如,用户容易在 UI 执行动画或者滑动 ListView 的时候感知到卡顿不流畅,是因为这里的操作相对复杂,容易发生丢帧的现象,从而感觉卡顿。有很多原因可以导致丢帧,也许是因为你的 layout 太过复杂,无法在 16ms 内完成渲染,有可能是因为你的 UI 上有层叠太多的绘制单元,还有可能是因为动画执行的次数过多。这些都会导致 CPU 或者 GPU 负载过重。

注:关于Android 显示刷新机制和 VSYNC 信号,请参考前文《Android 显示刷新机制、VSYNC和三重缓存机制》。

渲染与 CPU 和 GPU 的关系


由于 CPU 和 GPU 的设计不同,CPU 更擅长复杂逻辑控制,而 GPU 得益于大量 ALU 和并行结构设计,更擅长数学运算。在 Android 中,页面由各种基础元素(DisplayList)构成,渲染时需要进行大量浮点运算,这些工作更适合交给 GPU 来做。Android 为了提高视图渲染的性能,在 Android 3.0 中引入了硬件加速。这样,Android 就有两种绘制模型:基于软件的绘制模型、硬件加速绘制模型。关于绘制模型,请参考前文《Android 渲染机制——绘制模型》。

在 Android 系统中,CPU 与 GPU 的分工不同,CPU 主要负责包括 Measure,Layout,Record,Execute 的计算操作,GPU 主要负责 Rasterization(栅格化)操作。栅格化是指将向量图形格式表示的图像转换成位图(像素)以用于显示设备输出的过程,简单来说就是将我们要显示的视图,转换成用像素来表示的格式。栅格化是一个非常耗时的工作。

CPU 与 GPU 职责对比

为了简单说明 CPU 和 GPU 的职责工作,请看下图:

在这里插入图片描述

该图简单说明CPU和GPU的职责工作,以及可能发生的问题和解决方案。

  • CPU 产生的问题:不必要的布局和失效

    在 CPU 方面,最常见的性能问题是不必要的布局和失效,这些内容必须在视图层次结构中进行测量、清除并重新创建。引发这种问题通常有两个原因:一是重建显示列表的次数太多,二是花费太多时间作废视图层次并进行不必要的重绘,这两个原因在更新显示列表或者其他缓存 GPU 资源时导致 CPU 工作过度。

  • GPU 产生的问题:过度绘制(overdraw)

    在 GPU 方面,最常见的问题是我们所说的过度绘制(overdraw),通常是在像素着色过程中,通过其他工具进行后期着色时浪费了 GPU 处理时间。下面我们对 GPU 和 CPU 产生的两大问题进行优化。

GPU

Android 是如何利用 GPU 进行画面渲染的呢?

一个很直接的问题是:Activity 的画面是如何绘制到屏幕上的?那些复杂的 XML 布局文件又是如何能够被识别并绘制出来的?

在这里插入图片描述

Resterization 栅格化是绘制那些 Button,Shape,Path,String,Bitmap 等组件最基础的操作。它把那些组件拆分到不同的像素上进行显示。这是一个很费时的操作,GPU 的引入就是为了加快栅格化的操作。

CPU

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

在这里插入图片描述

这里想一个问题:CPU 是如何将数据传递给 GPU 的呢?

每次从 CPU 转移到 GPU 是一件很麻烦的事情,所幸的是 OpenGL ES 可以把那些需要渲染的纹理 Hold 在 GPU Memory 里面,在下次需要渲染的时候直接进行操作。所以如果你更新了 GPU 所 Hold 住的纹理内容,那么之前保存的状态就丢失了。

在 Android 里面那些由主题所提供的资源,例如 Bitmaps,Drawables 都是一起打包到统一的 Texture 纹理当中,然后再传递到 GPU 里面,这意味着每次你需要使用这些资源的时候,都是直接从纹理里面进行获取渲染的。当然随着 UI 组件的越来越丰富,有了更多演变的形态。例如显示图片的时候,需要先经过 CPU 的计算加载到内存中,然后传递给 GPU 进行渲染。文字的显示比较复杂,需要先经过 CPU 换算成纹理,然后交给 GPU 进行渲染,返回到 CPU 绘制单个字符的时候,再重新引用经过 GPU 渲染的内容。动画则存在一个更加复杂的操作流程。

Android 渲染机制


Android 系统采用一种称为 Surface 的 UI 架构为应用程序提供用户界面。在 Android 应用程序中,每一个 Activity 组件都关联有一个或者若干个窗口,每一个窗口都对应有一个 Surface。有了这个 Surface 之后,应用程序就可以在上面渲染窗口的 UI。最终这些已经绘制好了的 Surface 都会被统一提交给 Surface 管理服务 SurfaceFlinger 进行合成,最后显示在屏幕上面。无论是应用程序,还是 SurfaceFlinger,都可以利用 GPU 等硬件来进行 UI 渲染,以便获得更流畅的 UI。

一句话来概括一下 Android 应用程序显示的过程:Android 应用程序调用 SurfaceFlinger 服务把经过测量、布局和绘制后的 Surface 渲染到显示屏幕上。

我们来看几个重要的成员:

  • ViewRootImpl:用来控制窗口的渲染,以及用来与 WindowManagerService、SurfaceFlinger 通信。
  • WindowManager:WindowManager 会控制窗口对象,它们是用于容纳视图对象的容器。窗口对象始终由 Surface 对象提供支持。WindowManager 会监督生命周期、输入和聚焦事件、屏幕方向、转换、动画、位置、变形、Z 轴顺序以及窗口的许多其他方面。WindowManager 会将所有窗口元数据发送到 SurfaceFlinger,以便 SurfaceFlinger 可以使用这些数据在屏幕上合成 Surface。
  • Surface:Android 应用的每个窗口对应一个画布(Canvas),即 Surface,可以理解为 Android 应用程序的一个窗口。Surface 是一个接口,供生产方与使用方交换缓冲区。
  • SurfaceView:SurfaceView 是一个组件,可用于在 View 层次结构中嵌入其他合成层。SurfaceView 采用与其他 View 相同的布局参数,因此可以像对待其他任何 View 一样对其进行操作,但 SurfaceView 的内容是透明的。当 SurfaceView 的 View 组件即将变得可见时,框架会要求 SurfaceControl 从 SurfaceFlinger 请求新的 Surface。
  • BufferQueue:BufferQueue 类将可生成图形数据缓冲区的组件(生产方)连接到接受数据以便进行显示或进一步处理的组件(使用方)。几乎所有在系统中移动图形数据缓冲区的内容都依赖于 BufferQueue。
  • SurfaceFlinger:Android 系统服务,负责管理 Android 系统的帧缓冲区,即显示屏幕。
  • EGLSurface 和 OpenGL ES:OpenGL ES (GLES) 定义了用于与 EGL 结合使用的图形渲染 API。EGI 是一个规定如何通过操作系统创建和访问窗口的库(要绘制纹理多边形,请使用 GLES 调用;要将渲染放到屏幕上,请使用 EGL 调用)。
  • Vulkan:Vulkan 是一种用于高性能 3D 图形的低开销、跨平台 API。与 OpenGL ES 一样,Vulkan 提供用于在应用中创建高质量实时图形的工具。

Android 图形架构图

Android 图形架构图,显示了关键组件如何协同工作:

在这里插入图片描述

图形系统使用生产者-消费者模式,根据框架图,可以看出基本的操作流程:

图形生产者创建一个 Surface,将图形数据画到 Surface 上。Surface 使用的图形缓冲是通过 Gralloc 来分配的。 图形生产者的窗口信息由 WindowManager 管理,WindowManager 将窗口元数据发送给 SurfaceFlinger 进行合成。 图形消费者处理图形生产者产生的数据。当图形数据用于屏幕显示时,SurfaceFlinger 使用窗口元数据将图形缓冲合成到显示上。图形数据最终渲染到显示设备上是通过 Hardware Composer 完成的。硬件一般支持多个显示层,如果显示层可以满足 Surface 的需求,则可以将所有可见 Surface 发送给 Hardware Composer 进行合成。也可以使用 GPU 预先完成一些 Surface 数据的合成工作,之后再送给 Hardware Composer。

图像流生产方

图像流生产方可以是生成图形缓冲区以供消耗的任何内容。例如 OpenGL ES、Canvas 2D 和 mediaserver 视频解码器。

图像流消耗方

图像流的最常见消耗方是 SurfaceFlinger,该系统服务会消耗当前可见的 Surface,并使用窗口管理器中提供的信息将它们合成到显示部分。SurfaceFlinger 是可以修改所显示部分内容的唯一服务。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 来合成一组 Surface。

其他 OpenGL ES 应用也可以消耗图像流,例如相机应用会消耗相机预览图像流。非 GL 应用也可以是使用方,例如 ImageReader 类。

硬件混合渲染器

显示子系统的硬件抽象实现。SurfaceFlinger 可以将某些合成工作委托给 Hardware Composer,以分担 OpenGL 和 GPU 上的工作量。SurfaceFlinger 只是充当另一个 OpenGL ES 客户端。因此,在 SurfaceFlinger 将一个或两个缓冲区合成到第三个缓冲区中的过程中,它会使用 OpenGL ES。这样使合成的功耗比通过 GPU 执行所有计算更低。

Hardware Composer HAL 则进行另一半的工作,并且是所有 Android 图形渲染的核心。Hardware Composer 必须支持事件,其中之一是 VSYNC(另一个是支持即插即用 HDMI 的热插拔)。

Gralloc 用来分配图形生产方请求的内存。

Gralloc

需要使用图形内存分配器 (Gralloc) 来分配图像生产方请求的内存。通常由 SurfaceFlinger 申请并将显示内容存储到缓存中,供 HAL 使用。

接下来,我们来看,Android 是如何将一个 xml 布局渲染到屏幕上的。我们可以将渲染过程简单分为两个部分:应用程序侧绘制和系统侧渲染。

应用侧绘制

一个 Android 应用程序窗口里面包含了很多 UI 元素,它们是以树形结构来组织的,父视图包含子视图,一个窗口的根视图是 DecorView 对象,ViewRootImpl 负责与系统进程进行通信。

在绘制一个 Android 应用程序窗口的 UI 之前,我们首先要确定它里面的各个子 UI 元素在父 UI 元素里面的大小以及位置。确定各个子 UI 元素在父 UI 元素里面的大小以及位置的过程又称为测量过程和布局过程。因此,Android 应用程序窗口的 UI 渲染过程可以分为测量、布局和绘制三个阶段,最后生成 Display List。

如下图所示:

在这里插入图片描述

  1. 测量:递归(深度优先)确定所有视图的大小(高、宽)
  2. 布局:递归(深度优先)确定所有视图的位置(左上角坐标)
  3. 绘制:在画布 canvas 上绘制应用程序窗口所有的视图
  4. 生成 Display List 数据。

测量、布局负责确定每个视图组件的大小和位置信息,接下来就是绘制了。

Android 有两种绘制模型:基于软件的绘制模型和硬件加速的绘制模型。参见:《Android 渲染机制——绘制模型》。

Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象。这个操作是在 DisplayList 的帮助下完成的。Display List 持有所有将要交给 GPU 绘制到屏幕上的数据信息。

关于 Display List 相关内容,请参考前文:《Android 渲染机制——Display List》。

系统侧渲染

1. Display List 数据交由 GPU 进行渲染处理

Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象。这个操作是在 DisplayList 的帮助下完成的。Display List 持有所有将要交给 GPU 绘制到屏幕上的数据信息。

Display List 是一个缓存绘制命令的 Buffer,Display List 的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。渲染 Display List,发生在应用程序进程的 Render Thread 中。增加 Render Thread 线程,也是为了避免 UI 线程任务过重,用于提高渲染性能。

这些绘制命令最终会转化为 Open GL 命令由 GPU 执行。这意味着我们在调用 Canvas API 绘制 UI 时,实际上只是将 Canvas API 调用及其参数记录在 Display List 中,然后等到下一个 VSYNC 信号到来时,记录在 Display List 里面的绘制命令才会转化为 Open GL 命令由 GPU 执行。

2. GPU 渲染处理

Android 使用 OpenGL ES (GLES) API 渲染图形。GPU 将视图栅格化后,生成 Surface。GPU 作为图像流生产方将显示内容,最终发送给图像流的消耗方 SurfaceFlinger。

大多数客户端使用 OpenGL ES 或 Vulkan 渲染到 Surface 上(硬件加速,使用了 GPU 渲染)。但是,有些客户端使用画布渲染到 Surface 上(未使用硬件加速)。

3. 生成 Surface 并存储到 BufferQueue

Surface 对象使应用能够渲染要在屏幕上显示的图像。通过 SurfaceHolder 接口,应用可以编辑和控制 Surface。Surface 是一个接口,供生产方与使用方交换缓冲区。

SurfaceHolder 是系统用于与应用共享 Surface 所有权的接口。与 Surface 配合使用的一些客户端需要 SurfaceHolder,因为用于获取和设置 Surface 参数的 API 是通过 SurfaceHolder 实现的。一个 SurfaceView 包含一个 SurfaceHolder。与 View 交互的大多数组件都涉及到 SurfaceHolder。如果你开发的是相机相关的应用,会涉及到相关 API 的使用。

通常 SurfaceFlinger 使用的缓冲区队列是由 Surface 生成的,当渲染到 Surface 上时,结果最终将出现在传送给消费者的缓冲区中。Canvas API 提供一种软件实现方法(支持硬件加速),用于直接在 Surface 上绘图(OpenGL ES 的低级别替代方案)。与视图有关的任何内容均涉及到 SurfaceHolder,其 API 可用于获取和设置 Surface 参数(如大小和格式)。

BufferQueue 是 SurfaceFlinger 使用的缓冲区队列,而 Surface 是 BufferQueue 的生产方。BufferQueue 类将可生成图形数据缓冲区的组件(生产方)连接到接受数据以便进行显示或进一步处理的组件(使用方,例如 SurfaceFlinger)。几乎所有在系统中移动图形数据缓冲区的内容都依赖于 BufferQueue。

用于显示 Surface 的 BufferQueue 通常配置为三重缓冲。缓冲区是按需分配的,因此,如果生产方足够缓慢地生成缓冲区(例如在 60 fps 的显示屏上以 30 fps 的速度进行缓冲),队列中可能只有两个分配的缓冲区。按需分配缓冲区有助于最大限度地减少内存消耗。

数据处理过程:

在这里插入图片描述

上图描述了图形管道的流程。左侧为图形生产者,右侧为图形消费者,中间通过 BufferQueues 连接。图中,主屏幕、状态栏和系统界面通过 GPU 渲染生成图形缓冲区,做为生产者传递给 BufferQueues。SurfaceFlinger 做为消费者,接收到 BufferQueues 的通知后,取出可用的图形缓冲区,送给显示端。例图中将状态栏和系统界面的图形缓冲送给 GPU 合成,生成新的图形缓冲后再通过 BufferQueues 发送到硬件混合渲染器。

注意:SurfaceFlinger 创建并拥有 BufferQueue 数据结构,并且可存在于与其生产方不同的进程中。

4. SurfaceFlinger 将显示数据发送给显示屏

SurfaceFlinger 接受来自多个源的数据缓冲区,然后将它们进行合成并发送到显示屏。WindowManager 为 SurfaceFlinger 提供缓冲区和窗口元数据,而 SurfaceFlinger 可使用这些信息将 Surface 合成到屏幕。

SurfaceFlinger 可通过两种方式接受缓冲区:通过 BufferQueue 和 SurfaceControl,或通过 ASurfaceControl。上文中,我们已经介绍了 BufferQueue。ASurfaceControl 是 Android 10 新增的,这是 SurfaceFlinger 接受缓冲区的另一种方式。ASurfaceControl 将 Surface 和 SurfaceControl 组合到一个事务包中,该包会被发送至 SurfaceFlinger。ASurfaceControl 与层相关联,应用可通过 ASurfaceTransactions 更新该层。然后,应用可通过回调(用于传递包含锁定时间、获取时间等信息的 ASurfaceTransactionStats)获取有关 ASurfaceTransactions 的信息。

我们来看 SurfaceFlinger 和 WMS 之间的交互过程:

SurfaceFlinger 接受缓冲区的一种方式是通过 BufferQueue 和 SurfaceControl。当应用进入前台时,它会从 WindowManager 请求缓冲区。然后,WindowManager 会从 SurfaceFlinger 请求层。层是 surface(包含 BufferQueue)和 SurfaceControl(包含屏幕框架等层元数据)的组合。SurfaceFlinger 创建层并将其发送至 WindowManager。然后,WindowManager 将 Surface 发送至应用,但会保留 SurfaceControl 来操控应用在屏幕上的外观。

在屏幕处于两次刷新之间时,屏幕会向 SurfaceFlinger 发送 VSYNC 信号。VSYNC 信号表明可对屏幕进行刷新而不会产生撕裂。当 SurfaceFlinger 接收到 VSYNC 信号后,SurfaceFlinger 会遍历其层列表,以查找新的缓冲区。如果 SurfaceFlinger 找到新的缓冲区,SurfaceFlinger 会获取缓冲区;否则,SurfaceFlinger 会继续使用上一次获取的那个缓冲区。SurfaceFlinger 必须始终显示内容,因此它会保留一个缓冲区。如果在某个层上没有提交缓冲区,则该层会被忽略。

SurfaceFlinger 在收集可见层的所有缓冲区之后,便会询问硬件混合渲染器 (HWC) 应如何进行合成。如果 HWC 将层合成类型标记为客户端合成,则 SurfaceFlinger 将合成这些层。然后,SurfaceFlinger 会将输出缓冲区传递给 HWC。

注:SurfaceFlinger 更详细的说明,请查看前文《Android 渲染机制——SurfaceFlinger》。

总结


  1. 帧率是以帧为单位的位图图像每秒连续出现在显示器上的次数(速率)。简单来说就是一秒钟,屏幕显示多少张画面。

  2. Android 在设计的时候,把帧频限定在了每秒 60 帧,当我们的 APP 的帧频 60fps 时,画面就会非常的流畅。

  3. 界面刷新渲染依赖底层的 VSYNC 信号,VSYNC 信号以每秒 60 次的频率发送给上层,并且高于 60fps 的帧频也是没有必要的,因为人眼与大脑之间的协作无法感知超过 60fps 的画面更新。

  4. 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能,也就是 16ms 内没有办法把这一帧的任务执行完毕,发生了丢帧的现象。

  5. 由于 CPU 和 GPU 的设计不同,CPU 更擅长复杂逻辑控制,而 GPU 得益于大量 ALU 和并行结构设计,更擅长数学运算。在 Android 系统中,CPU 与 GPU 的分工不同,CPU 主要负责包括 Measure,Layout,Record,Execute 的计算操作,GPU 主要负责 Rasterization(栅格化)操作。

  6. Android 为了提高视图渲染的性能,在 Android 3.0 中引入了硬件加速。

  7. 在 Android 应用程序中,每一个 Activity 组件都关联有一个或者若干个窗口,每一个窗口都对应有一个 Surface。有了这个 Surface 之后,应用程序就可以在上面渲染窗口的 UI。最终这些已经绘制好了的 Surface 都会被统一提交给 Surface 管理服务 SurfaceFlinger 进行合成,最后显示在屏幕上面。无论是应用程序,还是 SurfaceFlinger,都可以利用 GPU 等硬件来进行 UI 渲染,以便获得更流畅的 UI。

  8. 一句话来概括一下 Android 应用程序显示的过程:Android 应用程序调用 SurfaceFlinger 服务把经过测量、布局和绘制后的 Surface 渲染到显示屏幕上。

  9. 图形生产者创建一个 Surface,将图形数据画到 Surface 上。Surface 使用的图形缓冲是通过 Gralloc 来分配的。 图形生产者的窗口信息由 WindowManager 管理,WindowManager 将窗口元数据发送给 SurfaceFlinger 进行合成。 图形消费者处理图形生产者产生的数据。当图形数据用于屏幕显示时,SurfaceFlinger 使用窗口元数据将图形缓冲合成到显示上。图形数据最终渲染到显示设备上是通过 Hardware Composer 完成的。

  10. Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象。这个操作是在 DisplayList 的帮助下完成的。Display List 持有所有将要交给 GPU 绘制到屏幕上的数据信息。

  11. Display List 是一个缓存绘制命令的 Buffer,Display List 的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。渲染 Display List,发生在应用程序进程的 Render Thread 中。增加 Render Thread 线程,也是为了避免 UI 线程任务过重,用于提高渲染性能。

  12. Android 使用 OpenGL ES (GLES) API 渲染图形。GPU 将视图栅格化后,生成 Surface。GPU 作为图像流生产方将显示内容,最终发送给图像流的消耗方 SurfaceFlinger。

  13. BufferQueue 是 SurfaceFlinger 使用的缓冲区队列,而 Surface 是 BufferQueue 的生产方。BufferQueue 类将可生成图形数据缓冲区的组件(生产方)连接到接受数据以便进行显示或进一步处理的组件(使用方,例如 SurfaceFlinger)。

  14. SurfaceFlinger 接受来自多个源的数据缓冲区,然后将它们进行合成并发送到显示屏。WindowManager 为 SurfaceFlinger 提供缓冲区和窗口元数据,而 SurfaceFlinger 可使用这些信息将 Surface 合成到屏幕。


**PS:更多精彩内容,请查看 --> 《Android 性能优化》
**PS:更多精彩内容,请查看 --> 《Android 性能优化》
**PS:更多精彩内容,请查看 --> 《Android 性能优化》