2026-10-13 更新:本文原写于 2014 年,源码节选已对照 AOSP
android-4.4.4_r2。Overdraw 的概念仍适用,ADT、monitor、Hierarchy Viewer 和旧 OpenGL 跟踪入口已过时;Android Studio 3.1 起改用 Layout Inspector 查看层级,帧时序使用 Perfetto。下文保留历史源码,并补充 Android 12 / Android 16 的对应边界。
博客有一段时间没有更新了,到了新公司后,一直比较忙,博客也更新地不那么频繁了。最近一直在看Android上和性能相关的部分,也就是所谓的Android性能优化,才发现Android性能这一块,自己懂得还是太少了,所以从上层开始看,也算是一点一点入门吧。这个系列将讲解学习过程中总结的和性能相关的内容。
先讲讲 GPU 过度绘制,也是开发者最直接接触的部分吧。这个内容分两篇来讲:第一篇讲原理和一些优化建议,第二篇用实际例子来看一般的优化步骤。
过度绘制概念
GPU 过度绘制(Overdraw)指同一帧内,一个像素被绘制多次。例如 TextView 先画背景再画文字,文字覆盖到的像素就可能参与两次绘制。多余的像素着色、混合和内存访问会消耗资源,但是否造成掉帧,取决于绘制面积、透明度、GPU 能力以及这一帧的其他工作,不能仅凭颜色数量判断。Android 过度绘制说明
现在排查可以先用开发者选项中的 Debug GPU Overdraw 找到重复绘制区域,再用 Layout Inspector 对照实际层级和背景。Compose 的重组次数与像素过度绘制是不同指标,不能互相代替。
修改后,在相同设备、刷新率和操作下关闭调试着色再比较帧时序。Android 12(API 31)及以上可使用 Perfetto FrameTimeline 对照应用帧和 SurfaceFlinger 帧;该数据源目前不支持 SurfaceView。应用内部少画一层背景,并不自动证明 SurfaceFlinger 合成耗时也下降了。
过度绘制的原因
- 多个背景或自定义绘制覆盖了同一块区域,其中一部分完全被不透明内容遮住。
- 半透明内容需要与下层像素混合,不能简单跳过被它覆盖的绘制。
- 同一绘制过程重复提交了没有可见贡献的图形。
View 数量、嵌套深度和 inflate 时间需要另外分析。一个没有背景也不绘制内容的父容器会增加布局工作,但不一定增加像素过度绘制;一个扁平的自定义 View 也可以在同一像素上画很多次。
过度绘制和不合理的 XML 布局的影响
- inflate 读取编译后的布局资源,创建 View 并设置属性。这主要消耗 CPU;节点数量、控件构造和属性处理都会影响耗时,不等于 GPU 已经绘制了多少层。
- 布局变化可能触发 measure/layout,界面更新还要记录和执行绘制命令。并非每一帧都重新 inflate 或重新执行完整布局;需要从 trace 中确认实际耗时发生在创建、测量、布局、绘制还是等待阶段。
过度绘制的一些基本概念
在支持的设备上打开「开发者选项 → 调试 GPU 过度绘制 → 显示过度绘制区域」。原文的魅族拨号代码和菜单位置属于当年的机型,不作为现在启用开发者选项的通用步骤。这里主要观察 HWUI 绘制的应用窗口,不能把颜色图当作整机所有 Surface 的完整 GPU 计数器。
默认配色中的次数要区分「总绘制次数」和「额外绘制次数」。以本文核对的 Android 4.4.4 实现为例:
| 颜色 | 同一像素的总绘制次数 | 超过首次绘制的次数 |
|---|---|---|
| 原色,无调试着色 | 1 | 0 |
| 蓝色 | 2 | 1 |
| 绿色 | 3 | 2 |
| 淡红色 | 4 | 3 |
| 红色 | 5 及以上 | 4 及以上 |
蓝色也有过度绘制。我当时给的检查建议是「控制在 2x、不允许 4x、大面积 3x 不超过四分之一屏幕」,这不是 Android 的通用验收标准。颜色可以帮助找可疑区域,值不值得改,还要看帧耗时和视觉需求;文字、圆角、阴影和半透明效果可能需要叠加绘制。
优化工具介绍
1. Lint 工具
Android Studio 的 lint 可以检查布局和代码中的问题,也可以通过项目的 Gradle lint 任务运行。编辑器中的提示不代表完整 lint 任务已经执行,具体规则和严重级别以项目的 Android Gradle Plugin / lint 版本与配置为准。参见官方 lint 使用说明。
下面保留的是早期工具截图,不是当前 Android Studio 的界面:

2. Lint 布局建议及边界
- Compound drawables:如果图片与文本不需要独立的布局、点击或无障碍语义,可以用 TextView 的 compound drawable 减少节点。
- Merge root frame:仅当根容器没有需要保留的背景、padding、布局或交互行为,并且有合适的宿主父容器时,才考虑
<merge>。它不是可独立实例化的 View。 - Useless leaf / parent:先确认节点没有自定义绘制、前景、点击、动画或无障碍作用。没有背景本身不足以证明节点可以删除。
- Deep layouts:嵌套可能增加测量与布局开销。官方布局优化文档列出的默认深度警告阈值为 10,项目可以受版本和配置影响;它是静态检查线索,不是超过 10 层就一定掉帧的结论。
这些规则主要面向 View/XML。修改前后还要检查布局结果是否一致,不能为了消除警告改变界面语义。优化布局层级
3. Hierarchy Viewer 与 Layout Inspector
Hierarchy Viewer 已停止维护。它曾在 Android Device Monitor 中展示视图树及 measure/layout/draw 的相对耗时;旧版能否连接设备取决于系统构建、调试能力和连接协议,不是磁盘是否加密。原文通过 ViewServer 适配旧工具的做法仅供历史参考。
Android Studio 3.1 及以上使用 Layout Inspector 检查运行时层级和属性。它不能替代帧性能测量;定位耗时仍需要 system trace、帧指标或应用自己的测量数据。
布局优化建议
- 简单横向或纵向布局可以用 LinearLayout;复杂 View 布局可以比较 ConstraintLayout 等方案。不要仅按容器类名给性能排序,也不要把 View/XML 的测量经验直接套到 Compose。
- 用
<include>复用布局;满足宿主要求时,用<merge>去掉没有行为的中间容器。 - 对首次显示时不需要的内容,用 ViewStub 延后创建。已经存在的 View 只切换可见性,不需要再次创建对象;因此「动态 inflate 一定比 setVisibility 快」不成立。ViewStub 会把工作推迟到首次展开时,并非消除这些工作。
- 删除确定没有可见贡献的背景。只在上层始终不透明且完整覆盖下层的区域才安全;还要检查列表空白、滚动边缘、加载、错误、点击和转场状态。
- selector 的 normal 状态可以在视觉允许时不绘制背景,但不能一律改成透明,也不能同时丢掉选中、按压和无障碍反馈。
- 嵌套使用
layout_weight的 LinearLayout 可能需要多轮测量。这是 measure 阶段的成本,不等于所有子 View 都必定在 draw 阶段执行两次;结合 MeasureSpec 与实际 trace 判断。 - 减少无意义的节点和重复测量,同时保留控件语义。优化目标是降低已测出的成本,而不是让层级图看起来最扁平。
源码相关
有兴趣看源码的同学,可以接着看看 AOSP android-4.4.4_r2 的 OpenGLRenderer.cpp。下面是两段节选,依赖 HWUI 内部状态:renderOverdraw() 把 stencil 计数转换成调试颜色,countOverdraw() 读取调试结果并计算均值。
1 | void OpenGLRenderer::renderOverdraw() { |
glReadPixels() 是这个旧调试计数实现的一部分,不是应用应在每帧额外执行的优化步骤,也不代表 GPU 毫秒耗时。
原文提到的数值叠层位于同一标签的 frameworks/base/core/java/android/view/HardwareRenderer.java。下面保留 debugOverdraw() / drawOverdrawCounter() 节选;AOSP android-5.0.0_r1 的该文件已没有这两个方法,不能在当前系统照搬这个入口:
1 | private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty, |
这两段代码说明 Android 4.4.4 的实现,不能作为现代 HWUI 源码路径。以 AOSP Android 16 的 android-16.0.0_r1 为例,调试过度绘制已经由 SkiaPipeline.cpp 中的 Properties::debugOverdraw 分支调用 renderOverdraw()。阅读设备源码时先固定对应标签,再追踪实际渲染后端。
参考文章
- 原优化案例(历史参考,本次未能重新访问)
- 反编译并添加gpu显示
- https://developer.android.com/develop/ui/views/layout/improving-layouts/optimizing-layouts
- https://developer.android.com/develop/ui/views/layout/improving-layouts/reusing-layouts
- https://developer.android.com/develop/ui/views/layout/improving-layouts/loading-ondemand
- RecyclerView 文档(原 Smooth Scrolling 链接已重定向至此)
- https://developer.android.com/studio/profile/hierarchy-viewer
- https://developer.android.com/studio/write/lint
关于我 && 博客
下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!
- 博主个人介绍 :里面有个人的微信和微信群链接。
- 本博客内容导航 :个人博客内容的一个导航。
- 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
- Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。
一个人可以走的更快 , 一群人可以走的更远
