全链路渲染流程
App 层(View.invalidate/OpenGL 绘制)→
Choreographer 同步 VSync →
ViewRootImpl.performTraversals →
Surface 写入 Buffer →
BufferQueue 共享内存 →
SurfaceFlinger 合成 → 屏幕显示
核心环节
- CPU 耗时环节:布局测量(Measure)、布局排版(Layout)、Canvas 绘制、主线程阻塞;
- GPU 耗时环节:过度绘制、着色器计算、纹理采样、深度测试;
- 同步阻塞环节:VSync 对齐失败、Buffer 缓冲区阻塞、跨进程通信延迟。
CPU 渲染优化(针对 View/Canvas 绘制)
- 布局层级优化:减少 Measure/Layout 耗时
核心问题:布局嵌套过深(如 LinearLayout 嵌套多层)会导致 onMeasure/onLayout 递归调用次数暴增,耗时呈指数级上升。
优化方案
使用 ConstraintLayout 扁平化布局:用一个 ConstraintLayout 替代多层嵌套的 LinearLayout/RelativeLayout,将布局层级控制在 2-3 层以内。
避免过度使用 wrap_content:wrap_content 会触发多次测量,优先使用 match_parent 或固定尺寸。
复用布局:用 <include> 标签复用公共布局,用 <merge> 标签减少多余的父布局层级。
- 布局层级优化:减少 Measure/Layout 耗时
- 绘制优化:减少 onDraw 耗时
核心问题:onDraw 中频繁创建对象(如 Paint、RectF)、执行复杂计算,会触发 GC 卡顿;全屏重绘会导致 CPU 像素填充过载。
优化方案
复用绘制资源:Paint、RectF、Path 等对象全局初始化,避免在 onDraw 中创建。
局部重绘:用 invalidate(Rect) 替代 invalidate(),只刷新变化的区域。
关闭硬件加速的坑点:部分 Canvas API(如 drawTextOnPath)在硬件加速下会异常,需针对性关闭(setLayerType(LAYER_TYPE_SOFTWARE, null)),但尽量全局开启硬件加速。
使用 Bitmap 缓存复杂绘制:将高频重复的绘制内容(如背景图案)预渲染到 Bitmap,onDraw 中直接绘制 Bitmap。
- 绘制优化:减少 onDraw 耗时
- 主线程优化:避免阻塞
核心问题:主线程执行网络请求、数据库查询、复杂计算,会阻塞 Choreographer 的 doFrame 回调,导致掉帧。
优化方案
耗时任务移到子线程:用 Thread/Coroutine/RxJava 处理耗时操作,避免 ANR 和绘制阻塞。
延迟初始化非首屏 UI:首屏加载时只初始化关键 View,非首屏 View 用 ViewStub 懒加载。
减少主线程同步锁:避免在主线程执行加锁操作,防止线程阻塞。
- 主线程优化:避免阻塞
GPU 渲染优化(针对 OpenGL/GLSurfaceView 绘制)
- 减少过度绘制(Overdraw)
核心问题:GPU 重复绘制同一像素区域(如多层重叠的 View / 纹理),会耗尽 GPU 填充率,导致掉帧。
优化方案
开启过度绘制调试:开发者选项 → 调试 GPU 过度绘制,观察屏幕颜色:
蓝色(1 倍):正常;绿色(2 倍):可接受;红色(4 倍以上):必须优化。
移除无用背景:删除 android:background 重复设置的布局,避免透明层叠加。
OpenGL 面剔除:启用 GLES20.glEnable(GLES20.GL_CULL_FACE),剔除立方体背面不可见的面,减少一半的绘制量。
- 减少过度绘制(Overdraw)
- 着色器代码优化
核心问题:片段着色器执行次数等于像素数,复杂的 for 循环、三角函数会导致 GPU 计算过载。
优化方案
减少片段着色器计算:将复杂计算(如矩阵变换、光照计算)移到顶点着色器(顶点数远小于像素数)。
避免分支语句:GLSL 中 if-else 会破坏 GPU 并行计算,尽量用数学公式替代。
使用低精度类型:对精度要求不高的变量,用 lowp(低精度)替代 highp(高精度),减少 GPU 计算量。
- 着色器代码优化
- 纹理资源优化
核心问题:大尺寸纹理会占用大量显存,纹理格式不当会增加 GPU 采样耗时。
优化方案
压缩纹理格式:使用 ETC2、ASTC 等 GPU 支持的压缩格式,减少纹理内存占用(如 1080P 图片从 4MB 压缩到 1MB)。
纹理尺寸幂次化:GPU 对 2 的幂次尺寸纹理(如 256×256、512×512)采样效率更高,非幂次纹理会被 GPU 自动拉伸,增加开销。
复用纹理 ID:避免频繁创建 / 销毁纹理,用完的纹理通过 glDeleteTextures 释放显存。
- 纹理资源优化
- 缓冲区优化
核心问题:频繁更新顶点缓冲区数据会导致 CPU-GPU 数据传输阻塞。
优化方案
使用顶点缓冲对象(VBO):将顶点数据缓存到 GPU 显存中,减少 CPU-GPU 数据传输次数。
批量绘制:将多个小图形的顶点数据合并到一个缓冲区,用一次 glDrawArrays 绘制,减少 OpenGL API 调用次数。
- 缓冲区优化
同步环节优化(VSync + BufferQueue)
- 对齐 VSync 信号
核心问题:绘制耗时超过 VSync 间隔(60Hz 下 16.6ms,120Hz 下 8.3ms),会导致帧丢失,画面卡顿。
优化方案
监控帧耗时:用 Choreographer.FrameCallback 监控每帧耗时,定位超过 VSync 间隔的帧。
拆分长任务:将耗时超过 16.6ms 的绘制任务拆分成多帧执行,避免单次绘制阻塞 VSync。
- 对齐 VSync 信号
- 优化 BufferQueue 策略
核心问题:Buffer 数量不足或缓冲区阻塞,会导致 SurfaceFlinger 无可用数据合成,出现闪屏 / 卡顿。
优化方案
合理设置 Buffer 数量:通过 SurfaceHolder.setFixedSize 固定 Surface 尺寸,避免 Buffer 频繁重建;高频场景(如视频)使用三缓冲替代双缓冲。
避免 Buffer 长时间锁定:SurfaceHolder.lockCanvas() 后尽快绘制并调用 unlockCanvasAndPost(),防止 Buffer 被占用。
- 优化 BufferQueue 策略
性能调试工具实战
- 必用工具 1:GPU 呈现模式分析
开启方式:开发者选项 → GPU 呈现模式分析 → 选择 “在屏幕上显示为条形图”。
解读方式
绿色条:CPU 耗时;红色条:GPU 耗时;蓝色条:同步耗时。
条形高度不超过 16.6ms(60Hz) 为正常,超过则表示对应环节耗时过高。
- 必用工具 1:GPU 呈现模式分析
- 必用工具 2:Systrace
核心作用:抓取全链路渲染轨迹,定位 CPU/GPU 耗时瓶颈。
使用步骤
命令行执行:python systrace.py -t 10 -o trace.html gfx view sched(抓取 10 秒数据)。
运行 App 并复现卡顿场景。
用 Chrome 打开 trace.html,分析 Choreographer#doFrame、View#draw、OpenGL#glDrawArrays 等关键函数的耗时。
- 必用工具 2:Systrace
- 必用工具 3:Android Studio Profiler
CPU Profiler:查看主线程 / 子线程的函数调用耗时,定位主线程阻塞点。
Memory Profiler:监控内存抖动,定位 onDraw 中的对象频繁创建问题。
GPU Profiler:分析 OpenGL 着色器耗时、纹理内存占用。
- 必用工具 3:Android Studio Profiler