核心角色
- CPU (中央处理器): 就像一个全能但比较忙的“工程师”。它擅长逻辑计算、数据处理,但做大量重复性的绘图工作(比如画圆、填充颜色、叠加图层)效率不高,比较累。
- GPU (图形处理器): 就像一个专业的“画师”。它天生就是为处理大量图形运算而设计的,拥有成百上千个核心,可以同时处理很多简单的绘图任务(像素填充、纹理映射、几何变换),速度非常快,功耗也相对低。
硬件加速的基本思想: 把屏幕上绘制界面的繁重工作,尽可能地从 CPU 交给 GPU 去做。这样 CPU 就能腾出手来处理更重要的任务(比如响应你的点击、处理网络数据、运行应用逻辑),界面绘制也会更流畅、更省电。
一、 它是如何使用的?(开发者角度)
对大多数普通开发者来说,硬件加速在 Android 3.0 (API 11) 及以上版本是默认全局开启的! 这意味着你通常什么都不用做,系统就已经在尽力使用 GPU 来绘制你的界面了。
开发者可以控制的层面
虽然默认开启,但你可以在不同层级上调整硬件加速的行为:
-
Application 级别 (AndroidManifest.xml):
<application android:hardwareAccelerated="true" ...> ... </application>
- 这是默认设置(
true
)。整个 APP 都尝试使用硬件加速。 - 设置为
false
会强制整个 APP 使用软件绘制(CPU绘制),强烈不推荐,除非有极特殊兼容性问题。
- 这是默认设置(
-
Activity 级别 (AndroidManifest.xml):
<activity android:hardwareAccelerated="false" ... />
- 可以针对某个特定的 Activity 关闭硬件加速。比如这个 Activity 里有一个非常老的、只能在 CPU 下正常工作的自定义 View。
-
Window 级别 (在代码中):
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED );
- 在 Activity 的
onCreate()
中调用,为该 Activity 的窗口开启硬件加速(如果 Application 或 Activity 级别没关闭的话)。 - 注意:在 Android 4.0 (API 14) 及以上,硬件加速默认开启,这个标志主要用于兼容旧版本或非常特定的场景。
- 在 Activity 的
-
View 级别 (在代码中):
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); // 强制此 View 使用软件绘制 (CPU) myView.setLayerType(View.LAYER_TYPE_HARDWARE, null); // 强制此 View 使用硬件绘制 (GPU) - 注意:不是所有操作都支持!
- 这是开发者最常用的控制点!
LAYER_TYPE_SOFTWARE
:强制这个特定的 View(及其子 View)使用 CPU 绘制,绕过 GPU。常用于解决兼容性问题(某些自定义 View 的绘制操作在 GPU 路径下会崩溃或显示异常)。LAYER_TYPE_HARDWARE
:明确要求系统为这个 View 创建一个离屏的 GPU 缓冲区(纹理)。这主要用于优化复杂动画(如旋转、缩放、透明度变化)。把这个复杂的 View 预先画在 GPU 的一块“画板”(纹理)上,动画时只需要移动/变换这块“画板”,效率极高,避免了动画过程中每一帧都重新绘制整个复杂 View。但这会消耗额外的显存。null
参数通常是一个可选的Paint
对象。
总结使用: 作为开发者,大部分时间你什么都不用做(享受默认加速)。当你遇到特定 View 绘制异常时,尝试用 setLayerType(View.LAYER_TYPE_SOFTWARE, null)
把它切回 CPU 绘制。当你要优化一个复杂 View 的动画性能时,可以尝试 setLayerType(View.LAYER_TYPE_HARDWARE, null)
利用离屏缓冲(但要权衡内存消耗)。
二、 原理 (内部如何工作?)
想象一下绘制一帧画面的过程(比如屏幕刷新时):
-
录制绘图命令 (Record Drawing Commands - CPU):
-
APP 的 UI 线程(通常是主线程)开始工作。
-
系统遍历整个 View 树(View Hierarchy),从根 View 开始。
-
对于每个需要绘制或更新的 View,调用它的
onDraw(Canvas canvas)
方法。 -
关键变化(硬件加速下): 这个
Canvas
对象不再是直接操作屏幕像素的软件Canvas
。它是一个特殊的DisplayListCanvas
(或类似的硬件加速 Canvas) 。 -
当你在
onDraw
里调用canvas.drawRect()
,canvas.drawText()
,canvas.drawBitmap()
等方法时:- 软件绘制 (CPU): 这些调用会立即在 CPU 上执行计算,修改代表屏幕一部分的软件位图(
Bitmap
)的像素。 - 硬件绘制 (GPU): 这些调用不会立即执行绘制!它们被录制成一系列 GPU 能够理解的绘图指令(Display List)。这就像 CPU 在写一个详细的“绘图清单”给 GPU:“先画一个红色的矩形在 (10,10) 位置,大小 100x100;然后在上面画一张图片...”。这个录制过程非常高效。
- 软件绘制 (CPU): 这些调用会立即在 CPU 上执行计算,修改代表屏幕一部分的软件位图(
-
-
执行绘图命令 (Render the Frame - GPU):
-
当一帧的所有 View 的绘制命令都录制完成(整个 Display List 构建好),UI 线程的工作就基本完成了。
-
系统(主要是
RenderThread
渲染线程)将这个打包好的“绘图清单”(Display List)和相关的资源(图片、形状路径数据等)提交给 GPU 驱动程序。 -
GPU 闪亮登场:
- GPU 拿到这份“清单”和资源。
- 它利用其强大的并行计算能力,非常快速地执行清单上的所有绘图指令。
- GPU 将最终的计算结果(合成好的像素)写入帧缓冲区。
- 屏幕的显示控制器(Display Controller)会定时(比如每秒 60 次 - 60fps)从帧缓冲区读取数据,显示到屏幕上。
-
核心优势
- CPU 解放: CPU 只需要高效地录制命令(写清单),不再需要做繁重的像素计算,可以更快地响应应用逻辑和用户交互。
- GPU 高效: GPU 是图形处理的专家,执行大量简单的图形指令(如填充、变换、合成)速度极快,功耗比 CPU 做同样工作要低。
- 并行处理: CPU 录制下一帧命令的同时,GPU 可能还在处理上一帧的渲染。
RenderThread
的出现使得渲染工作可以更好地与主线程解耦。 - 流畅动画: 对于
LAYER_TYPE_HARDWARE
的 View,动画时只需要 GPU 变换预渲染好的纹理(那块“画板”),避免了昂贵的逐帧重绘,动画无比顺滑。
软件绘制 vs. 硬件加速绘制
特性 | 软件绘制 (CPU) | 硬件加速绘制 (GPU) |
---|---|---|
执行者 | CPU | GPU |
Canvas | 软件 Canvas (操作 Bitmap ) | DisplayListCanvas (录制命令) |
onDraw | 立即执行绘制操作,修改像素 | 录制绘图指令到 Display List |
绘制过程 | CPU 直接计算像素值 | GPU 根据 Display List 执行绘图指令 |
动画性能 | 复杂 View 动画时可能卡顿(需逐帧重绘全部) | 离屏缓冲 (LAYER_TYPE_HARDWARE ) 动画极流畅 |
兼容性 | 支持所有 2D 绘制操作 | 部分复杂操作(如自定义 Path、某些滤镜)可能不支持 |
内存 | 通常较少额外内存 | 离屏缓冲 (LAYER_TYPE_HARDWARE ) 消耗额外显存 |
功耗 | 绘制复杂时较高 | 图形密集型任务通常更低 |
三、 源码调用 (简化流程)
理解源码流程有助于看清本质,但不需要死记硬背。核心流程如下:
-
起点:
ViewRootImpl
- 这是连接 WindowManager 和 View 系统的关键类。
- 在
ViewRootImpl#draw(boolean fullRedrawNeeded)
方法中,开始绘制流程。
-
硬件加速入口:
ThreadedRenderer
- 硬件加速绘制的核心实现类(Android 4.0+)。
ViewRootImpl
会获取或创建一个ThreadedRenderer
实例 (mThreadedRenderer
)。- 调用
ThreadedRenderer#initialize()
初始化与 GPU 的连接(通过 EGL 等图形 API)。
-
创建硬件加速 Canvas:
DisplayListCanvas
- 在
ThreadedRenderer#startFrame()
或类似准备阶段,会创建一个DisplayListCanvas
。 - 这个特殊的 Canvas 就是用来录制绘图命令的。
- 在
-
遍历 View 树 & 录制命令:
ViewRootImpl
调用View.draw(Canvas)
,传入的是DisplayListCanvas
。View.draw()
内部会调用onDraw(Canvas)
。- 你在
onDraw
里调用的canvas.drawXxx()
方法,最终会调用到DisplayListCanvas
的对应方法(如drawRect()
)。 DisplayListCanvas.drawRect()
等: 这些方法不会画任何东西!它们只是把绘图操作(画什么、在哪里、什么颜色、用什么画笔)记录到内部的DisplayList
数据结构中。这本质上是构建了一个 GPU 命令列表。
-
同步 & 提交:
- 当整个 View 树的绘制命令录制完成(
View.draw()
结束),ViewRootImpl
会做必要的同步(比如等待动画值更新)。 - 调用
ThreadedRenderer#draw(View, AttachInfo, DrawCallbacks)
。 - 在
ThreadedRenderer
内部,会将构建好的DisplayList
(可能包含根 View 的 DisplayList 和它子 View 的 DisplayList)、以及相关的资源(纹理)提交给渲染线程 (RenderThread
) 和 GPU。
- 当整个 View 树的绘制命令录制完成(
-
GPU 执行:
RenderThread
RenderThread
是一个独立的、高优先级的线程,专门负责与 GPU 通信。- 它接收
ThreadedRenderer
提交过来的绘制数据。 - 通过 OpenGL ES 或 Vulkan 等图形 API,将
DisplayList
中的命令翻译成 GPU 能执行的指令。 - 驱动 GPU 执行这些指令,进行光栅化(计算像素)、纹理合成等操作。
- 最终将渲染结果输出到 SurfaceFlinger 管理的帧缓冲区。
-
显示:
SurfaceFlinger
- 系统服务 SurfaceFlinger 负责管理多个窗口的 Surface(帧缓冲区)。
- 它将各个窗口(包括你的 APP 窗口)最终合成好的帧缓冲区内容,按照 Z-order 叠加。
- 在 VSync 信号到来时,将最终合成的图像送到显示控制器,显示到屏幕上。
关键类总结
ViewRootImpl
: 绘制流程发起者和协调者。ThreadedRenderer
: 硬件加速渲染的核心控制器,连接 UI 线程和渲染线程。DisplayListCanvas
: 硬件加速下的特殊 Canvas,用于录制绘图命令到 DisplayList。RenderNode
/DisplayList
: 存储录制好的绘图命令的数据结构(每个 View 通常对应一个 RenderNode)。RenderThread
: 独立的渲染线程,负责与 GPU 通信并执行绘制命令。SurfaceFlinger
: 系统服务,负责合成多个窗口的最终画面并送显。
注意事项 & 常见问题
-
兼容性问题: 不是所有的
Canvas
或Paint
操作都被硬件加速路径完全支持。如果你在onDraw
里用了非常冷门或复杂的操作(尤其是一些自定义的 Path 效果、特定的混合模式、过时的 API),可能在开启硬件加速时出现崩溃、黑块或绘制错误。解决方法: 找到问题 View,用myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
将其切回软件绘制。 -
过度绘制: 硬件加速再快,也怕你让 GPU 画太多重叠的、看不见的东西。使用开发者选项中的 “显示过度绘制区域” 来诊断和优化。
-
内存消耗:
LAYER_TYPE_HARDWARE
会为 View 创建离屏纹理(GPU 内存)。滥用它(尤其是大 View 或多层)会导致显存紧张,反而可能引起卡顿或崩溃。用完后(动画结束),考虑用setLayerType(View.LAYER_TYPE_NONE, null)
释放资源。 -
调试工具:
- 开发者选项 -> 调试 GPU 过度绘制: 可视化过度绘制情况。
- 开发者选项 -> GPU 渲染模式分析 / Profile HWUI rendering: 显示每一帧各个阶段的耗时(如录制命令、等待 GPU、GPU 执行),定位瓶颈是 CPU 还是 GPU。
- Systrace: 强大的系统级性能分析工具,可以深入追踪 UI 线程、RenderThread、GPU 工作状态和耗时。
总结
- 是什么: 硬件加速就是把绘制手机界面的重活,从 CPU 转交给更专业的 GPU 去做。
- 怎么用: 默认开启!遇到问题 View 用
setLayerType(SOFTWARE)
切回 CPU;优化复杂动画用setLayerType(HARDWARE)
利用离屏缓冲。 - 原理: CPU 录制“绘图清单”(Display List),GPU 快速执行清单命令并合成画面。
- 优点: 界面更流畅(尤其动画),响应更快,图形任务更省电。
- 注意: 兼容性问题、避免过度绘制、谨慎使用离屏缓冲(内存消耗)。