微信公众号

Android 渲染优化

为什么优化

卡顿现象,由于复杂的布局或界面过度绘制未能在每帧16ms内完成导致的。

复杂的布局

Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,要每次渲染都成功,这样就必须达到流畅的画面所需要的60fps,否则会发生丢帧的现象,丢帧越多,用户明确感到卡顿。

补充:
1、fps,每秒显示帧数,帧率测量单位(frames per second);
2、为什么是60fps(16ms=1000/60)?因为人眼与大脑之间的协作无法感知超过60fps的画面更新。

过度绘制

Overdraw(过度绘制)是指系统在单个渲染帧中多次绘制屏幕上的像素。例如,如果我们有一堆堆叠的UI卡,不可见的UI也在做绘制的操作,这样会浪费大量的CPU和GPU资源。

补充:
渲染操作通常依赖于两个核心组件:CPU与GPU。CPU负责包括Measure,Layout,Record,Execute的计算操作,GPU负责Rasterization(栅格化)操作。

如何检测

Show GPU Overdraw

打开Show GPU Overdraw选项进行观察是否存在过度绘制。
步骤:
设置 -> 开发者选项 -> 调试GPU过度绘制 ->显示过度绘制区域。
对比一张Overdraw的参考图,分别有蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况:

蓝色: 意味着overdraw 1倍,像素绘制了两次;
绿色: 意味着overdraw 2倍,像素绘制了三次;
淡红: 意味着overdraw 3倍,像素绘制了四次;
深红: 意味着overdraw 4倍,像素绘制了五次或者更多。

我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。

Profile GPU Rendering

打开Profile GPU Rendering,显示每帧画面所需要渲染的时间。
步骤:
设置 -> 开发者选项 -> GPU呈现模式分析 -> 在屏幕上显示为条形图

界面上会滚动显示垂直的柱状图来表示每帧画面所需要渲染的时间,柱状图越高表示花费的渲染时间越长。中间有一根绿色的横线,代表16ms,我们需要确保每一帧花费的总时间都低于这条横线,这样才能够避免出现卡顿的问题。

Hierarchy Viewer

用Hierarchy Viewer工具检查Activity中的布局是否过于复杂
步骤:
Tools -> Android -> Android Device Monitor。

打开Hierarchy Viewe:
启动Android Device Monitor成功之后,在新的的窗口中点击切换视图图标,选择Hierarchy Viewe:

使用Hierarchy Viewer:

其实中带有红色或黄色的点代表速度较慢的View。

友情提示:
APP先运行起来再使用Android Device Monitor,建议用模拟器,手机可能读不到内容。

TraceView

使用TraceView来观察CPU执行情况,使用详见:性能分析工具 Android TraceView

优化方案

删除不必要的布局背景

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/backgroundColor"
android:orientation="vertical">
<com.wuxiaolong.pullloadmorerecyclerview.PullLoadMoreRecyclerView
android:id="@+id/pullLoadMoreRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

如果这里PullLoadMoreRecyclerView也设置背景色是没有必要了。

另外,使用Android一些自带的主题,window被默认添加一个纯色的背景,theme中添加android:windowbackground=”null”除掉或添加自己需要的背景色,减少渲染。

优化布局层次

通过优化视图层次结构,以减少重叠的UI对象的数量来提高性能。
这里得纠正我一个错误,我平时不想使用RelateLayout,是因为不想每个都命名id,命名是件很头疼的事,所以我更多使用了LinearLayout布局。为了提高性能,还是尽量多使用RelativeLayout吧。

使用include、merge、ViewStub

1、include布局重用;
2、merge减少视图层级;
3、ViewStub标签是当你需要时才会加载
详细介绍见:Android抽象布局——include、merge 、ViewStub

自定义组件的onDraw()

1、避免大量创建临时对象,比如String,以免频繁触发GC;
2、考虑使用canvas.clipRect()绘制需要被绘制的区域。

ListView

1、考虑使用ViewHolder;
2、或者RecycleView来代替

Lint

Lint是一个代码扫描工具,能够帮助我们识别代码结构存在的问题。在布局文件上运行lint工具来搜索可能的视图层次结构优化是一种很好的做法。
步骤:Android Studio,Analyze -> Inspect Code。
布局信息将显示在Android> Lint> Performance下:

要查看更多详细信息,您可以单击每个项目来展开它,并在屏幕右侧的窗格中查看更多信息。
lint规则:

  • Use compound drawables - A LinearLayout which contains an ImageView and a TextView can be more efficiently handled as a compound drawable.
  • Merge root frame - If a FrameLayout is the root of a layout and does not provide background or padding etc, it can be replaced with a merge tag which is slightly more efficient.
  • Useless leaf - A layout that has no children or no background can often be removed (since it is invisible) for a flatter and more efficient layout hierarchy.
  • Useless parent - A layout with children that has no siblings, is not a ScrollView or a root layout, and does not have a background, can be removed and have its children moved directly into the parent for a flatter and more efficient layout hierarchy.
  • Deep layouts - Layouts with too much nesting are bad for performance. Consider using flatter layouts such as RelativeLayout or GridLayout to improve performance. The default maximum depth is 10.

Lint除了layout优化,还能检查编码、可访问性等问题。

参考

官方Rendering

官方Improving Layout Performance

Android性能优化之渲染篇

Android性能优化(一)-布局优化-%E5%B8%83%E5%B1%80%E4%BC%98%E5%8C%96/)