深入了解 Android的硬件加速

9 阅读10分钟

核心角色

  1. CPU (中央处理器):  就像一个全能但比较忙的“工程师”。它擅长逻辑计算、数据处理,但做大量重复性的绘图工作(比如画圆、填充颜色、叠加图层)效率不高,比较累。
  2. GPU (图形处理器):  就像一个专业的“画师”。它天生就是为处理大量图形运算而设计的,拥有成百上千个核心,可以同时处理很多简单的绘图任务(像素填充、纹理映射、几何变换),速度非常快,功耗也相对低。

硬件加速的基本思想:  把屏幕上绘制界面的繁重工作,尽可能地从 CPU 交给 GPU 去做。这样 CPU 就能腾出手来处理更重要的任务(比如响应你的点击、处理网络数据、运行应用逻辑),界面绘制也会更流畅、更省电。

一、 它是如何使用的?(开发者角度)

对大多数普通开发者来说,硬件加速在 Android 3.0 (API 11) 及以上版本是默认全局开启的!  这意味着你通常什么都不用做,系统就已经在尽力使用 GPU 来绘制你的界面了。

开发者可以控制的层面

虽然默认开启,但你可以在不同层级上调整硬件加速的行为:

  1. Application 级别 (AndroidManifest.xml):

    <application android:hardwareAccelerated="true" ...> ... </application>
    
    • 这是默认设置(true)。整个 APP 都尝试使用硬件加速。
    • 设置为 false 会强制整个 APP 使用软件绘制(CPU绘制),强烈不推荐,除非有极特殊兼容性问题。
  2. Activity 级别 (AndroidManifest.xml):

    <activity android:hardwareAccelerated="false" ... />
    
    • 可以针对某个特定的 Activity 关闭硬件加速。比如这个 Activity 里有一个非常老的、只能在 CPU 下正常工作的自定义 View。
  3. Window 级别 (在代码中):

    getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
    );
    
    • 在 Activity 的 onCreate() 中调用,为该 Activity 的窗口开启硬件加速(如果 Application 或 Activity 级别没关闭的话)。
    • 注意:在 Android 4.0 (API 14) 及以上,硬件加速默认开启,这个标志主要用于兼容旧版本或非常特定的场景。
  4. 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) 利用离屏缓冲(但要权衡内存消耗)。

二、 原理 (内部如何工作?)

想象一下绘制一帧画面的过程(比如屏幕刷新时):

  1. 录制绘图命令 (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;然后在上面画一张图片...”。这个录制过程非常高效。
  2. 执行绘图命令 (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)
执行者CPUGPU
Canvas软件 Canvas (操作 Bitmap)DisplayListCanvas (录制命令)
onDraw立即执行绘制操作,修改像素录制绘图指令到 Display List
绘制过程CPU 直接计算像素值GPU 根据 Display List 执行绘图指令
动画性能复杂 View 动画时可能卡顿(需逐帧重绘全部)离屏缓冲 (LAYER_TYPE_HARDWARE) 动画极流畅
兼容性支持所有 2D 绘制操作部分复杂操作(如自定义 Path、某些滤镜)可能不支持
内存通常较少额外内存离屏缓冲 (LAYER_TYPE_HARDWARE) 消耗额外显存
功耗绘制复杂时较高图形密集型任务通常更低

三、 源码调用 (简化流程)

理解源码流程有助于看清本质,但不需要死记硬背。核心流程如下:

  1. 起点:ViewRootImpl

    • 这是连接 WindowManager 和 View 系统的关键类。
    • 在 ViewRootImpl#draw(boolean fullRedrawNeeded) 方法中,开始绘制流程。
  2. 硬件加速入口:ThreadedRenderer

    • 硬件加速绘制的核心实现类(Android 4.0+)。
    • ViewRootImpl 会获取或创建一个 ThreadedRenderer 实例 (mThreadedRenderer)。
    • 调用 ThreadedRenderer#initialize() 初始化与 GPU 的连接(通过 EGL 等图形 API)。
  3. 创建硬件加速 Canvas:DisplayListCanvas

    • 在 ThreadedRenderer#startFrame() 或类似准备阶段,会创建一个 DisplayListCanvas
    • 这个特殊的 Canvas 就是用来录制绘图命令的。
  4. 遍历 View 树 & 录制命令:

    • ViewRootImpl 调用 View.draw(Canvas),传入的是 DisplayListCanvas
    • View.draw() 内部会调用 onDraw(Canvas)
    • 你在 onDraw 里调用的 canvas.drawXxx() 方法,最终会调用到 DisplayListCanvas 的对应方法(如 drawRect())。
    • DisplayListCanvas.drawRect() 等:  这些方法不会画任何东西!它们只是把绘图操作(画什么、在哪里、什么颜色、用什么画笔)记录到内部的 DisplayList 数据结构中。这本质上是构建了一个 GPU 命令列表。
  5. 同步 & 提交:

    • 当整个 View 树的绘制命令录制完成(View.draw() 结束),ViewRootImpl 会做必要的同步(比如等待动画值更新)。
    • 调用 ThreadedRenderer#draw(View, AttachInfo, DrawCallbacks)
    • 在 ThreadedRenderer 内部,会将构建好的 DisplayList(可能包含根 View 的 DisplayList 和它子 View 的 DisplayList)、以及相关的资源(纹理)提交给渲染线程 (RenderThread) 和 GPU。
  6. GPU 执行:RenderThread

    • RenderThread 是一个独立的、高优先级的线程,专门负责与 GPU 通信。
    • 它接收 ThreadedRenderer 提交过来的绘制数据。
    • 通过 OpenGL ES 或 Vulkan 等图形 API,将 DisplayList 中的命令翻译成 GPU 能执行的指令。
    • 驱动 GPU 执行这些指令,进行光栅化(计算像素)、纹理合成等操作。
    • 最终将渲染结果输出到 SurfaceFlinger 管理的帧缓冲区。
  7. 显示:SurfaceFlinger

    • 系统服务 SurfaceFlinger 负责管理多个窗口的 Surface(帧缓冲区)。
    • 它将各个窗口(包括你的 APP 窗口)最终合成好的帧缓冲区内容,按照 Z-order 叠加。
    • 在 VSync 信号到来时,将最终合成的图像送到显示控制器,显示到屏幕上。

关键类总结

  • ViewRootImpl:  绘制流程发起者和协调者。
  • ThreadedRenderer:  硬件加速渲染的核心控制器,连接 UI 线程和渲染线程。
  • DisplayListCanvas:  硬件加速下的特殊 Canvas,用于录制绘图命令到 DisplayList。
  • RenderNode / DisplayList:  存储录制好的绘图命令的数据结构(每个 View 通常对应一个 RenderNode)。
  • RenderThread:  独立的渲染线程,负责与 GPU 通信并执行绘制命令。
  • SurfaceFlinger:  系统服务,负责合成多个窗口的最终画面并送显。

注意事项 & 常见问题

  1. 兼容性问题:  不是所有的 Canvas 或 Paint 操作都被硬件加速路径完全支持。如果你在 onDraw 里用了非常冷门或复杂的操作(尤其是一些自定义的 Path 效果、特定的混合模式、过时的 API),可能在开启硬件加速时出现崩溃、黑块或绘制错误。解决方法:  找到问题 View,用 myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null) 将其切回软件绘制。

  2. 过度绘制:  硬件加速再快,也怕你让 GPU 画太多重叠的、看不见的东西。使用开发者选项中的  “显示过度绘制区域”  来诊断和优化。

  3. 内存消耗:  LAYER_TYPE_HARDWARE 会为 View 创建离屏纹理(GPU 内存)。滥用它(尤其是大 View 或多层)会导致显存紧张,反而可能引起卡顿或崩溃。用完后(动画结束),考虑用 setLayerType(View.LAYER_TYPE_NONE, null) 释放资源。

  4. 调试工具:

    • 开发者选项 -> 调试 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 快速执行清单命令并合成画面。
  • 优点:  界面更流畅(尤其动画),响应更快,图形任务更省电。
  • 注意:  兼容性问题、避免过度绘制、谨慎使用离屏缓冲(内存消耗)。