-
View过度绘制,导致某些像素在同一帧被绘制很多次
-
在UI线程中做了稍微耗时的操作
-
GC回收时暂停时间过长或者频繁的GC产生大量的暂停时间
为了解决上述的问题,除了我们要在写代码时注意外,也可以借助一些工具来分析和解决卡顿问题。
Profile GPU Rendering是Android4.1系统提供的开发辅助功能,我们可以在开发者选项中打开这一个功能。
单击 Profile GPU Rendering -> On screen as bars(中文版是 GPU渲染模式分析 -> 在屏幕上显示为条形图):
其中横轴代表时间,纵轴代表某一帧的耗时。绿色的横线为警戒线,超过这条线则意味着市场超过了了16ms,尽量要保证垂直的彩色柱状图保持在绿线下面,这些垂直的彩色柱状图代表着一帧,不同颜色的彩色柱状图代表不同的含义。
,具体如下:
他们的含义为:
-
Swap Buffers:表示处理的时间,和上面讲到的橙色一样。
-
Command Issue:表示执行的时间,和上面讲到的红色一样
-
Sync & Upload:表示的是准备当前界面上有待绘制的图片所耗费的时间,
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
为了减少该段区域的执行时间,我们可以减少屏幕上的图片数量或者是缩小图片的大小。
-
Draw:表示测量和绘制视图列表所需要的时间,和上面讲到的蓝色一样。
-
Measure/Layout:表示布局的onMeasure与onLayout所花费的时间,一旦时间过长,就需要仔细检查自己的布局是不是存在严重的性能问题。
-
Animation:表示计算执行动画所需要花费的时间,包含的动画有ObjectAnimator,ViewPropertyAnimator,Transition等。一旦这里的执行时间过长,就需要检查是不是使用了非官方的动画工具或者是检查动画执行的过程中是不是触发了读写操作等等。
-
Input Handling:表示系统处理输入事件所耗费的时间,粗略等于对事件处理方法所执行的时间。一旦执行时间过长,意味着在处理用户的输入事件的地方执行了复杂的操作。
-
Misc Time/Vsync Delay:表示在主线程执行了太多的任务,导致UI渲染跟不上VSYNC的信号而出现掉帧的情况。
Profile GPU Rendering可以找到渲染有问题的界面,但是想要修复的话,只依赖Profile GPU Rendering是不够的,可以用另外一个工具Hierarchy Viewer来查看布局层次和每个View所花的时间,这个工具会在后面介绍。
Systrace是Android4.1新增的性能数据采样和分析工具,它可以帮助开发者手机Android关键子系统(SufaceFlinger、WMS等Framework部分关键模块、服务,View体系系统等)的运行信息。
Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。
所以可以用其来分析 UI渲染。
关于Systrace的使用:
Systrace跟踪的设备要在Android4.1上使用,其中4.3版本前后有区别,因为现在很少有4.3之前的手机了,所以就讲4.3之后的使用方法。
Systrace可以再DDMS上使用,可以使用命令行来使用,也可以在代码中进行跟踪。接下来介绍3种方式
1.在DDMS种使用Systrace
因为Google认为DDMS已经不好用了,所以在Android Studio3.0就删除它了。
我们要去 Sdk\tools 下找到 monitor.bat,运行它就能看到Android Device Monitor了。
选中当前追踪的手机,点中图中框出的地方:
最上面的路径就是Systrace生成html的路径。
第二行就是跟踪的时间,可以自己设置。
点击ok后,就会去追踪当前应用。
2. 用命令行使用Systrace
Android提供一个Python脚本文件systrace.py,它位于Android SDK目录 /tools/systrace中,我们可以使用以下命令来使用Systrace:
C:>cd C:\Users\msn\AppData\Local\Android\Sdk\platform-tools\systrace
C:\Users\msn\AppData\Local\Android\Sdk\platform-tools\systrace>python systrace.py --time=10 -o newtrace.html sched gfx view wm
然后我的就提示:Systrace does not support Python 3.7. Please use Python 2.7.
…
也就是说Android Studio通过命令行使用Python查看Systrace,要用2.7版本的…
3. 在代码中使用Systrace
可以使用Trace类对应用中的具体活动进行追踪。Android源码中也引入了Trace类,为了展示得更加直观,这里RecyclerView得源码:
void scrollStep(int dx, int dy, @Nullable int[] consumed) {
this.startInterceptRequestLayout();
this.onEnterLayoutOrScroll();
//这里开始使用了Trace进行追踪
TraceCompat.beginSection(“RV Scroll”);
this.fillRemainingScrollValues(this.mState);
int consumedX = 0;
int consumedY = 0;
if (dx != 0) {
consumedX = this.mLayout.scrollHorizontallyBy(dx, this.mRecycler, this.mState);
}
if (dy != 0) {
consumedY = this.mLayout.scrollVerticallyBy(dy, this.mRecycler, this.mState);
}
//这里开始暂停了Trace追踪
TraceCompat.endSection();
this.repositionShadowingViews();
this.onExitLayoutOrScroll();
this.stopInterceptRequestLayout(false);
if (consumed != null) {
consumed[0] = consumedX;
consumed[1] = consumedY;
}
}
TraceCompat
类对 Trace
类进行了封装,其中 beginSection()
和 endSection()
方法之间的代码会被追踪,endSection()
只会结束最近的 beginSection()
,因此要保证这个两个方法调用的次数要相同。
用Chrome分析Systrace
通过前面的方法生成了trace.html文件后,我们用Chrome打开它,如下图所示:
我们可以使用W、S键进行放大和缩小,A、D键进行左右移动。接下来介绍上图中的各个区域:
1.Alert区域
这个区域会标记性能有问题的点,单击叹号图标就是查看某一个Alert的问题描述,如下图所示:
这里指出:Scheduling delay说明这个处理特定时间片的线程很长时间没有被CPU调度,因此这个线程花了很久才完成。为什么会出现这种状况呢?可能是因为开启了太多的线程和UI线程竞争CPU资源,导致UI线程迟迟不能执行。
2.CPU区域
CPU区域,每一行代表一个CPU核心和它执行任务的时间片,放大后会看到每个色块代表一个执行过程,色块的长度表示执行时间。
CPU0主要执行adbb线程和InputReader线程,CPU 1主要执行surfaceflinger线程和ordinartorlayout进程中的RnderThread线程,我们单击每个色块,都能看到它做了什么事情,用了多久。
3. 应用区域
Systrace会给出应用中的Frames分析,每一帧就是一个F圈圈,F圈圈有三种颜色,绿色表示渲染流畅,黄色和红色则代表渲染的时间超过了16ms,红色更严重一点。图中的红色F点击后会告诉我们原因。
4. Alerts总体分析
单击最右边的Alerts按钮会给出Alert的总体分析:
Alerts会给出Alert类型,以及出现的次数。有了这些总体的分析,方便开发者对该间断的绘制性能有一个大概的了解,便于进行下一步分析。
由于Systrace是以系统的角度返回一些信息的,只能为我们提供一个概览,它的深度是有限的,我们可以用它来进行粗略的检查,以便了解大概的情况,但是如果还要分析的更加详细,比如要找到是什么让CPU繁忙,某些方法的调用次数等,则还要借助另一个工具 Traceview。
TraceView是 AndroidSdk中自带的数据采集和分析工具,一般来说通过TraceView,我们可以得到两种数据:
-
单次执行耗时的方法
-
执行次数多的方法
如何使用Traceview
要分析traceview,首先要得到一个 trace文件。获取trace文件有两种方式:
- DDMS中使用
在Android Device Monitor中选择相应的进程,并单击Start Method Profiling
按钮
然后对应用中需要监控的点进行操作
最后单击 Stop Method Profiling
按钮,会自动跳到TraceView师徒
- 在代码中加入调试语句
Debug.startMethodTracing();
…
Debug.stopMethodTracing();
系统会在SD卡中生成trace文件,将trace文件导出并用SDK中的Traceview打开即可。当然这需要我们为应用程序加上写权限。
然后我们就能得到trace文件。
接着我们来分析一下trace文件:
=======================================================================
一个界面的测量和绘制是通过递归来完成的,减少布局的层数就会减少测量和绘制的时间,从而性能就会得到提升。
当然这只是布局优化的一方面。
在讲布局优化之前,我们先来学习两种布局优化的工具,分别是 Hierarchy Viewer和Android Lint
Hierarchy Viewer是Android SDK自带的可视化的调试工具,用来检查布局嵌套和绘制的时间。
我们通过 Android Device Monitor,在其中选择Hierarchy Viewer如下图:
打开后会给出一个Hierarchy Viewer窗口,在Hierarchy Viewer窗口中有4个子窗口,它们的作用如下:
- Windows
当前设备所有界面列表
- Tree View
将当前Activity 的所有View的层次按照高层到低层从左到右显示出来
- Tree Overview
全局概览,以缩略的形式显示
- Layout View
整体布局图,以手机屏幕上真是的位置呈现出来。
使用注意:
在Android的官方文档中提到:
To preserve security, Hierarchy Viewer can only connect to devices running a developer version of the Android system.
即:出于安全考虑,Hierarchy Viewer只能连接Android开发版手机或是模拟器(准确地说,只有ro.secure参数等于0且ro.debuggable等于1的android系统)。Hierarchy Viewer在连接手机时,手机上必须启动一个叫View Server的客户端与其进行socket通信。而在商业手机上,是无法开启View Server的,故Hierarchy Viewer是无法连接到普通的商业手机。
所以我们在使用Android Device Monitor
去连接真机/没有配置的模拟器 会提示: Unable to get view server version from device xxxx....
网上有很多教程,主要是通过Android逆向去替换系统文件,让系统通过可以调试的验证。
这边我的做法是使用 ViewServer的开源框架,在导入后,在Activity中的使用方法是:
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set content view, etc.
ViewServer.get(this).addWindow(this);
}
public void onDestroy() {
super.onDestroy();
ViewServer.get(this).removeWindow(this);
}
public void onResume() {
super.onResume();
ViewServer.get(this).setFocusedWindow(this);
}
}
然后再去重新打开Hierarchy Viewer,在手机中打开使用这个代码的Activity就能看到层级结构了:
在我们开发中加入这些代码,当界面完成时,我们可以将这些调试的代码注释掉。
通过单击每个view,我们可以知道view的 layout、mesure、draw的耗时。
一个合格的布局,它应该是层级合理的。即纵向多,横向少。
Android Lint是在ADT 16中提供的新工具,它是一个代码扫描工具,通过代码静态检查来发现代码出现的潜在问题,并给出优化建议。检查的范围主要有以下几点:
-
正确性:Correctness
-
安全性:Security
-
性能:Performance
-
可用性:Usablity
-
可达性:Accessiblility
-
国际化:Internationalization
其功能十分强大,这里我们只关注 XML布局检查,可以通过AS的 Analyze->Inspect Code 来配置检查的范围。
点击OK后,就会出现所有问题的采集,以及每个问题种类的个数:
通过点击每一项,我们就能知道哪里出现了问题,或者说哪里的代码/资源文件没有被引用到。
我们可以通过在 Setting -> Editor-> Inspections 中配置Lint检查的范围。
布局优化的方法有很多,主要包括 合理运用布局、Inculde、Merge和ViewStub。
1.合理运用布局
这个比较笼统。举个例子: