一、绘制优化
1.绘制原理
说到绘制优化,首先不得不提的就是绘制原理。我们知道,View的绘制流程有3个步骤,分别是measure、layout和draw,它们主要是运行在系统的应用框架层,而在底层则是由CPU来进行Measure、Layout、Record、Execute的数据计算工作,由GPU来进行栅格化、渲染。CPU和GPU通过共同操作图形驱动层维护的一个队列来完成绘制,即CPU将display list添加到该队列中,GPU从队列中取出数据进行绘制。
再来说说帧数,我们知道,画面在60fps时候不会感受到卡顿,如果低于60fps则会感受到卡顿,因此若要保持流畅,则屏幕需要在1秒内刷新60次,也就是说,需要将View绘制时长控制在16ms以内。
Android系统每隔16ms发出一种定时中断信号来触发对UI进行渲染。如果每次渲染都成功,就能够达到流畅画面的60fps,但是如果某个操作需要花费更多时间,那么在得到信号时就无法进行征程的渲染,这样就会发生丢帧现象。
2.卡顿原因
产生卡顿的原因主要有以下几点:
①布局Layout过于复杂,无法在16ms内完成渲染;
②同一时间动画执行的次数过多,导致CPU或GPU负载过重;
③View过度绘制,导致某些像素在同一时间内被绘制多次;
④在UI线程中做了稍微耗时的操作;
⑤GC回收时暂停时间过长或者频繁的GC产生大量的暂停时间。
3.使用GPU渲染模式分析工具进行分析
在Android4.1以上的设备中,以魅族Flyme8.1.5.0A为例(底层为Android9),在开发者选项中找到“监控”,将GPU渲染模式分析设为“在屏幕上显示为条形图”,这时候就会在屏幕出现彩色的柱状图,如下图所示:
对于每个可见应用,都将会显示一个图形。图中沿水平轴的每个竖条代表一个帧,每个竖条的高度表示渲染该帧所花的时间(以毫秒为单位),水平绿线表示 16 毫秒。要实现每秒 60 帧,代表每个帧的竖条需要保持在此线以下,当竖条超出此线时,可能会使动画出现暂停。
下表介绍了使用运行 Android 6.0 及更高版本的设备时分析器输出中某个竖条的每个区段(来源:Android开发者官网)。
4.0(API 级别 14)和 5.0(API 级别 21)之间的 Android 版本具有蓝色、紫色、红色和橙色区段。低于 4.0 的 Android 版本只有蓝色、红色和橙色区段。下表显示的是 Android 4.0 和 5.0 中的竖条区段。
共同都有的是橙色、红色以及蓝色。
- 橙色:代表处理的时间,是CPU告诉GPU渲染一帧的地方,是一个阻塞调用,因为CPU会一直等待GPU发出接到命令的回复,所以如果橙色柱状图很高,则表明GPU很繁忙;
- 红色:代表执行的时间,是Android进行2D渲染Display List的时间。如果红色柱状图很高,可能由于重新提交了视图而导致的,另外,复杂的自定义View也会导致红色的柱状图变高;
- 蓝色:代表测量绘制的时间,也就是需要多长时间去创建和更新Display List。如果蓝色柱状图很高,可能有许多自定义视图绘制,或者View的onDraw方法执行的工作很多。
4.使用系统跟踪Systrace工具进行分析
“系统跟踪”就是记录短时间内的设备活动。系统跟踪会生成跟踪文件,该文件可用于生成系统报告,对于UI显示性能,比如动画播放不流畅、渲染卡顿等问题提供了分析数据。
“系统跟踪”应用是一款用于将设备活动保存到跟踪文件的 Android 工具。在搭载 Android 10(API 级别 29)或更高版本的设备上,跟踪文件会以 Perfetto 格式保存;在搭载较低版本 Android 系统的设备上,跟踪文件会以 Systrace 格式保存。
Systrace 是平台提供的旧版命令行工具,可记录短时间内的设备活动,并保存在压缩的文本文件中。该工具会生成一份报告,其中汇总了 Android 内核中的数据,例如 CPU 调度程序、磁盘活动和应用线程。systace对检测应用UI表现非常有效,因为它可以分析代码和帧率来识别出问题区域,然后提出可能的解决方案。
Perfetto 是 Android 10 中引入的全新平台级跟踪工具。这是适用于 Android、Linux 和 Chrome 的更加通用和复杂的开源跟踪项目。与 Systrace 不同,它提供数据源超集,可以以 protobuf 编码的二进制流形式记录任意长度的跟踪记录。
Perfetto 和 Systrace 可交互使用。
由于目前使用Android 10以下的手机还很多,这里就以Systrace为例进行说明。操作步骤如下:
①从Android Studio下载并安装最新的Android SDK工具(可依次点击 Tools > SDK Manager进入 SDK Manager进行查看)。
②安装Python(https://www.python.org/)(由于脚本仅支持2.X版本,请下载2.7版本)并将其添加到工作站的PATH环境变量中,如下图所示:
然后安装pywin32模块,可以使用pip命令进行安装。
③将android-sdk/platform-tools/添加到PATH环境变量。此目录包含由systrace程序调用的Android调试桥二进制文件 (adb)(systrace 命令在 Android SDK 工具软件包中提供,并且可以在 android-sdk/platform-tools/systrace/ 中找到)。
④使用 USB 调试连接将搭载Android 4.3(API 级别 18)或更高版本的设备连接到开发系统。
⑤使用以下语法通过命令行运行 systrace来为应用生成 HTML 报告:
python systrace.py [options] [categories]
例如,以下命令会调用systrace来记录设备活动,并生成一个名为mynewtrace.html的HTML报告。
python systrace.py -o mynewtrace.html sched freq idle am wm gfx view \
binder_driver hal dalvik camera input res
可以运行以下命令来查看已连接设备支持的类别列表:
python systrace.py --list-categories
如果未指定任何类别或选项,systrace会生成包含所有可用类别的报告,并使用默认设置。
⑥使用Chrome打开mynewtrace.html文件进行分析。报告界面如下图所示:
我们可以使用W键和S键进行放大和缩小;使用A键和D键进行左右移动。
接下来我们来具体讲解下这个报告怎么看。
整体观察,可以看见页面被分为了四部分,从上到下分别是用户互动、CPU 活动、应用区域以及Alert区域。接下来就分别说下这四个部分。
- 用户互动:这一部分包含表示应用或游戏中的具体用户互动(例如点按设备屏幕)的条形图。这些互动可用作有用的时间标记。
- Alert区域:可以看到互动区上面有很多A圆圈,这些A圆圈表示性能有问题的点,单击任意问题点,都可以在Alert区域看到相关的问题描述,如下图所示:
这个Alert指出了View在Measure/Layout 时耗费了大量的时间,导致出现jank(同一帧绘制了多次)。给出的建议是避免在动画播放期间控制布局。
也可以单击最右边的Alerts按钮查看Alert的总体分析,如图所示:
单击任意Alert type项,用户互动区域会直观显示有问题的点。
Alert区域也会显示对应Alert type项的总体信息。
- CPU 活动:这一部分中,每一行代表一个CPU核心和它执行任务的时间片,放大后会看到每个色块代表一个执行的进程,色块的长度代表这个进程执行的时间,如下图所示:
单击CUP6中的RenderThread线程色块,会给出RenderThread线程的相关信息。
可以看到,上图给出了当前色块所运行的线程和进程、开启时间和持续时间等信息。 - 应用区域:这个区域会显示各个进程的帧数,如下图所示:
我们以其中的com.tencent.mm为例来进行具体说明,com.tencent.mm如下图所示:
Systrace 会给出应用中的Frames分析,每一帧就是一个F圆圈,F圆圈有三种颜色,其含义如下:
绿色:16.6 毫秒内渲染完毕;
黄色或红色:渲染时间超过16.6 毫秒。其中红色的更严重一些。
单击红色F圆圈,会给出该Frame的信息,如下图所示:
可以看到这一帧出现了三个问题,分别是Inflation during ListView recycling、Expensive measure/layout pass和long View#draw(),单击箭头可以查看详细信息。
下面是systrace对每一种警告类型的解释:
Scheduling delay:渲染一帧的工作被推迟了几个毫秒,从而导致了不合格。确保UI线程上的代码不会被其他线程上完成的工作阻塞,并且后台线程(例如,网络或位图加载)在android.os.Process#THREAD_PRIORITY_BACKGROUND中运行或更低,因此它们不太可能中断UI线程。
Expensive measure/layout pass:测量/布局花费了很长时间,导致掉帧,要避免在动画过程中触发重新布局。
Long View#draw():记录无效的绘图命令花费了很长时间,在View或Drawable自定义视图时,要避免做耗时操作,尤其是Bitmap的分配和绘制。
Expensive Bitmap uploads:修改或新创建Bitmap视图要传送给GPU,如果像素总数很大,这个操作会很耗时。因此在每一帧中要尽量减少Bitmap变更的次数。
Inefficient View alpha usage:将alpha设置为半透明值(0<alpha<1)会很耗性能,尤其是对大视图。所以最好短暂地使用alpha属性。
由于systrace是以系统的角度返回一些信息的,因此只能为我们提供一个概览,它的深度是有限的。我们可以用它来进行粗略的检查,以便了解大概的情况,但是如果要分析更为详细的信息,比如要找到是什么让CPU繁忙、某些方法的调用次数等,还需要借助另一个工具——Android Studio中提供的CPU分析器。我们可以生成跟踪日志,然后使用 CPU 分析器导入和检查这些日志。
5.使用CPU分析器工具进行分析
在Android Studio 3.2之前,一般会使用Traceview,但是由于Traceview日志记录无法很好地处理线程(如果线程在分析期间退出,不会发出线程名称(在 Android 5.1 及更高版本中已解决此问题);虚拟机会重复使用线程 ID。如果在一个线程停止时另一线程开始,这两个线程可能会获得同一 ID),所以已经在Android Studio 3.2中被弃用,取而代之的是CPU 性能剖析器。
CPU 性能剖析器不仅能够以图形的形式显示跟踪日志,更包含了Systrace的工作,因此,在Android Studio 3.2以后就可以使用CPU 性能剖析器完成这两项工作了。
我们接着Systrace的工作来做,生成一个跟踪日志。
5.1使用 Debug API 记录 CPU 活动
如果在开发过程中出现不好复现的问题,可以在代码中添加跟踪语句,如下所示:
// Starts recording a trace log with the name you provide. For example, the
// following code tells the system to start recording a .trace file to the
// device with the name "sample.trace".
Debug.startMethodTracing("sample");
...
// The system begins buffering the generated trace data, until your
// application calls <code><a href="/reference/android/os/Debug.html#stopMethodTracing()">stopMethodTracing()</a></code>, at which time it writes
// the buffered data to the output file.
Debug.stopMethodTracing();
在开始监控的地方调用startMethodTracing方法,在需要结束的地方调用stopMethodTracing方法,系统就会在~/sdcard/ 目录中生成trace文件,这个文件包含二进制方法跟踪数据,以及一个包含线程和方法名称的映射表。在进行跟踪的时候不要忘了在AndroidManifest.xml中个加入
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
权限。
我们来举个例子进行详细说明。
public class TraceSampleActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_trace_sample);
//开始跟踪,并且指定生成名为sample的.trace文件
Debug.startMethodTracing("sample");
initView();
}
private void initView(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onStop() {
super.onStop();
//结束监控
Debug.stopMethodTracing();
}
}
我们使用了一个简单的例子来生成trace文件,然后将这个文件导出到桌面。这个trace文件的位置如下图所示:
5.2导入跟踪数据
①打开 CPU 性能剖析器:依次选择 View > Tool Windows > Profiler 或点击工具栏中的 Profile 图标 。
②在性能剖析器的 Sessions 窗格中点击 Start new profiler session 图标 ,然后选择 Load from file。
可以看到分为了两部分,分别是左面的时间片面板和右面的分析面板。
时间面板放大,之后将鼠标指针悬停在某个线程上,可以看见更详细的信息。
一般我们会查看色块的长度,然后对明显比较长的方法重点去关注。单击色块可以在右面的分析面板查看具体分析。
更详细的操作可以查看谷歌开发者文档:
6.使用布局优化工具进行分析
6.1使用布局检查器和布局验证工具调试布局
在Android Studio 3.1之前,一般会使用Hierarchy Viewer分析布局,但是在Android Studio 3.1开始,Hierarchy Viewer则被弃用,改为了布局检查器和布局验证工具。
使用Android Studio中的布局检查器,可以将应用布局与设计模型进行比较、显示应用的放大视图或3D视图,以及在运行时检查应用布局的细节。如果布局是在运行时(而不是完全在XML中)构建的并且布局行为出现异常,该工具会非常有用。
使用布局验证,可以在不同的设备和显示配置(包括可变字体大小或用户语言)上同时预览布局,以便轻松测试各种常见的布局问题。
- 布局检查器
1.打开布局检查器:
a.在连接的设备或模拟器上运行应用;
b.依次点击 Tools > Layout Inspector,如下图所示,布局检查器将显示以下内容:
标号1:Component Tree:布局中视图的层次结构。
标号2:Layout Display:按照应用布局在设备或模拟器上的显示效果呈现布局,并显示 每个视图的布局边界。
标号3:布局检查器工具栏:布局检查器的工具。
标号4:Attributes:所选视图的布局属性。
2.选择视图:
如要选择某个视图,可以在Component Tree或Layout Display中点击该视图,所选视图的所有布局属性都会显示在Attributes面板中,如上图所示。
3.隔离视图:
如要使用复杂的布局,我们可以隔离各个视图,以便只有布局的一部分显示在 Component Tree 中并呈现在 Layout Display 中。在 Component Tree 中右键点击该视图,然后选择 Show Only Subtree 或 Show Only Parent。如需返回完整视图,右键点击该视图,然后选择 Show All。
4.隐藏布局边框和视图标签:
如果想要隐藏布局元素的边界框或视图标签,点击Layout Display顶部的View Options图标,然后切换Show Borders或Show View Label。
5.将应用布局与参考图像叠加层进行比较:
如果需将应用布局与参考图像(如界面模型)进行比较,可以在布局检查器中加载位图图像叠加层。
a.如需加载叠加层,点击布局检查器顶部的 Load Overlay 图标。系统会缩放叠加层以适合布局。
b.如需调整叠加层的透明度,使用 Overlay Alpha 滑块。
c.如需移除叠加层,点击 Clear Overlay 图标 。
6.实时布局检查器:
实时布局检查器可以在应用被部署到搭载 API 级别 29 或更高版本的设备或模拟器时,提供应用界面的完整实时数据分析。
要想启用实时布局检查器,需要进行如下操作:依次转到 File > Settings > Experimental,勾选 Enable Live Layout Inspector 旁边的框,然后点击 Layout Display 上方 Live updates 旁边的复选框。
实时布局检查器包含动态布局层次结构,可随着设备上视图的变化更新 Component Tree 和 Layout Display。
此外,使用属性值解析堆栈,也可以调查资源属性值在源代码中的来源位置,并按照属性窗格中的超链接导航到其位置,如下图所示。
最后,Layout Display 可在运行时对应用的视图层次结构进行高级 3D 可视化。如需使用该功能,只需在实时布局检查器窗口中点击相应布局,然后拖动鼠标旋转该布局即可。如需展开或收起布局的图层,使用Layer Spacing滑块。 - 布局验证工具
“布局验证”是一款可视化工具,用于同时预览不同设备中及采用不同配置的布局,有助于我们在此过程的早期发现布局存在的问题。如需使用该功能,在打开布局文件后,点击IDE窗口右上角的Layout Validation标签页:
如需在可用的配置集之间切换,可以从“Layout Validation”窗口顶部的下拉列表中选择以下某个配置:①Pixel Devices;②自定义;③色盲;④字体大小
1.Pixel Devices:
预览布局在 Pixel 设备上的显示效果,也是默认的选项,我们一进来看见的效果就是这个。
2.自定义:
从各种设置(包括语言、设备或屏幕方向)中进行选择自定义要预览的显示配置:
3.色盲:
为了方便色盲用户使用我们的应用,需要通过常见色盲类型的模拟验证布局: