文章目录
- 1. CPU与GPU协同工作原理
- 1.1. CPU和GPU架构
- 1.1.1. 画面显示过程
- 1.1.2. 没有Vsync
- 1.1.3. 有Vsync
- 1.1.4. 掉帧查看
- 2. CPU优化
- 2.1. 视图层次结构优化
- 2.2. \优化
- 2.3. \优化
- 2.4. \优化
- 3. GPU过度绘制
- 3.1. background优化
- 4. GPU呈现模式分析
1. CPU与GPU协同工作原理
1.1. CPU和GPU架构
- CPU:解析布局,生成View对象及位置属性,转化为多维矢量图形。
- GPU:将矢量图形栅格化,并在屏幕上绘制图形。
- CPU与GPU的桥梁:openGL ES。
1.1.1. 画面显示过程
Android系统会每隔16ms发出一次Vsync信号,类似于一个时间中断,系统在每次拿到Vsync信号时刷新屏幕,就不会觉得卡顿。
1.1.2. 没有Vsync
屏幕第2帧,CPU可能忙于别的任务(主线程任务太重),导致在第二个16ms结束时,也没有计算完数据,因此GPU也只能在第三个16ms才能计算完第2帧画面,此时就有两个16ms显示的相同画面,则画面出现了卡顿。
1.1.3. 有Vsync
每一次Vsync信息发出,CPU都在第一时间响应,来进行刷新,此时CPU和GPU的刷新时间,和显示屏的FPS是一致的,所以正常情况下是没有卡顿的。
1.1.4. 掉帧查看
Logcat 输入“Skipped”筛选系统日志,不过滤信息“No Filters”,不过滤层级“Verbose”
2. CPU优化
为了减少CPU在每一帧中布局解析的计算量,常用的方法有嵌套层级优化、优化、优化、优化。
2.1. 视图层次结构优化
针对复杂的布局,应该尽量让View的视图层次结构扁平化,层次结构越扁平,完成布局阶段所需的时间越少,Google 官方建议不要超过 10 层。
AndroidStudio ➡ Tools ➡ Layout Inspector,选择对应进程和对应Activity,查看布局树。
常用基础布局对比
布局 | 性能 | 嵌套层级 | 布局难度 |
FrameLayout | 好 | 低 | 难 |
LinearLayout | 好 | 高 | 易 |
RelativeLayout | 差 | 低 | 难 |
ConstraintLayout | 好 | 低 | 难 |
通常使用ConstraintLayout写布局的话,能有效减少布局层级,优化性能。
2.2. <include/>优化
通常我们可以将通用的布局提出到一个单独的Layout文件中,然后在需要的地方使用标签将其加载进来,例如导航栏等。
<include
android:layout_width="match_parent"
android:layout_height="40dp"
layout="@layout/titlebar" />
2.3. <merge/>优化
使用标签并不能直接减少嵌套层级。如果include
的父布局和被include
的根布局相同,且不需要设置background
、padding
等属性,则布局根标签可以使用标签代替。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:text="@string/app_name" />
</merge>
然后在需要的地方使用标签将其加载进来。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
// include的父布局,会作为merge的子布局的父布局
<include layout="@layout/merge_view" />
</RelativeLayout>
此时就能减少一层布局嵌套。
2.4. <ViewStub/>优化
同一样可以用来引入一个外部布局。如果引入的布局默认不显示也不占位置,则可以使用代替,从而在解析layout的时候可以节省CPU和内存资源。例如进度条、网络错误等提示性布局。
<ViewStub
android:id="@+id/network_error_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/network_error" />
在代码中,可以使用 (ViewStub)findViewById(id)
找到 ViewStub,通过 stub.inflate()
展开ViewStub,然后得到被引用的实例化对象。
ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout);
if(stub != null){
// 一旦inflate一次之后,id就失效了,则findViewById也找不到ViewStub了
// 这里保存之后,以后就不用也不能再inflate了
networkErrorView = stub.inflate();
}
3. GPU过度绘制
GPU过度绘制是在屏幕刷新的一帧中,同一个像素被绘制了多次。
这些组件从上到下分布,低层级的可能被高层级的遮挡,如果此时还花时间去绘制那些不可见的部分,就会出现过度绘制。
设置 ➡ 开发者选项 ➡ 调试GPU过度绘制 ➡ 显示过度绘制区域,就可以打开调试工具。
最理想的是白色和蓝色,即一个像素只绘制一次或两次,粉色以上区域面积不超过屏幕的三分之一,不能出现红色区域。
3.1. background优化
移除Activity的背景色
- 主题设置为
Theme.Translucent
及其子主题; - 自定义主题
windowIsTranslucent
为true
,windowBackground
为透明色;
移除Layout的背景色
-
android:background="@null"
或直接删除android:background
属性; -
android:background="@color/transparent"
,虽然透明了GPU不渲染,但实际会占用内存,只不过颜色是透明的,不建议;
裁剪Canvas
类似扑克牌游戏,位于底层的卡片并非全部露出,如果裁剪Canvas,则可以使被遮住的部分不渲染。
canvas.save();
canvas.clipRect(left, top, right, bottom);
// canvas.其他操作();
canvas.restore();
降低透明度
透明对象需要先绘制现有的像素,以便达到正确的混合效果。可以通过减少要渲染的透明对象的数量,来改善这些情况下的过度绘制。例如,要获得灰色文本,您可以在TextView中绘制黑色文本,再为其设置半透明的透明度值。但是,您可以简单地通过用灰色绘制文本来获得同样的效果,而且能够大幅提升性能。
4. GPU呈现模式分析
GPU 渲染模式分析工具以图表(以颜色编码的直方图)的形式显示各个阶段及其相对时间。
设置 ➡ 开发者选项 ➡ GPU/HWUI呈现模式分析 ➡ 在屏幕上显示为条形图,就可以打开调试工具。
竖条区段 | 渲染阶段 | 说明 |
Swap | 交换缓冲区 | 表示 CPU 等待 GPU 完成其工作的时间。如果此竖条升高,则表示应用在 GPU 上执行太多工作。 |
Issue | 发出命令 | 表示 Android 的 2D 渲染程序向 OpenGL 发出绘制和重新绘制显示列表的命令所花的时间。此竖条的高度与执行每个显示列表所花的时间的总和成正比。显示列表越多,红色竖条就越高。 |
Upload | 同步和上传 | 表示将位图信息上传到 GPU 所花的时间。大区段表示应用花费大量的时间加载图形,加载大量小型资源或少量大型资源。 |
Draw | 绘制 | 表示用于创建和更新视图显示列表的时间。如果竖条的此部分很高,表明可能有许多自定义视图绘制,或 onDraw 方法执行的工作很多。 |
Measure | 测量/布局 | 表示在视图层次结构中 [onLayout](https://developer.android.com/reference/android/view/View#onLayout(boolean, int, int, int, int)) 和 [onMeasure](https://developer.android.com/reference/android/view/View#onMeasure(int, int)) 回调上所花的时间。大区段表示处理视图层次结构需要很长时间,通常是由于需要布局的视图数量过多,或者出现了其他问题。 |
Anim | 动画 | 表示评估运行该帧的所有动画程序所花的时间。如果此区段很大,则表示您的应用可能在使用性能欠佳的自定义动画程序,或因更新属性而导致一些意料之外的工作。 |
Input | 输入处理 | 表示应用执行输入事件回调中的代码所花的时间。如果此区段很大,则表示应用花太多时间处理用户输入。不妨考虑将此类处理任务分流到其他线程。 |
Misc | 其他时间/VSync 延迟 | 表示应用执行两个连续帧之间的操作所花的时间。它可能表示界面线程中进行的处理太多,而这些处理任务本可以分流到其他线程。 |