App性能优化之View优化(1)——UI问题的检测

一.概述

开发中最常用到的优化方向:View优化、内存优化、耗电优化和稳定性优化等。无论用什么工具和方法,最后的结论就是我们要通过踩坑的经历总结经验,在以后的代码中不犯同样错误。本文介绍View在绘制过程中存在的问题的检测。

二.View的绘制过程中卡顿的产生

2.1基本知识点

1.Android绘制View

Android绘制View有三个主要的步骤,分别是measure、layout和draw。measure、layout和draw方法主要是运行在系统的应用框架层,而真正将数据渲染到屏幕上的则是系统Nativie层的SurfaceFlinger服务来完成的。这里不具体展开绘制原理,有兴趣自己去查。

2.FPS

FPS(Frames Per Second)这个名词我想很多同学都知道,它是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数,最简单的举例就是我们玩游戏时,如果画面在60fps则不会感觉到卡顿,如果低于60fps,比如50fps则会感觉到卡顿,你就可以考虑要换显卡或者采取其他一些措施了。要想画面保持在60fps,则需要每个绘制时长在16ms以内。

3.卡顿的产生

Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染, 如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps(VSYNC是Vertical Synchronization(垂直同步)的缩写,是一种定时中断,一旦收到VSYNC信号,CPU就开始处理各帧数据)。如果某个操作要花费25ms,这样系统在得到VSYNC信号时无法进行正常的渲染,会发生丢帧。用户会在32ms中看到同一帧的画面(丢一帧)。

2.2产生卡顿原因

产生卡顿原因有很多,主要有以下几点:

1.布局Layout过于复杂,无法在16ms内完成渲染。
2.同一时间动画执行的次数过多,导致CPU或GPU负载过重。
3.View过度绘制,导致某些像素在同一帧时间内被绘制多次。
4.UI线程中做了耗时的操作。

三.分析和解决卡顿问题的工具

  1. 布局层级检测——GPU过度绘制
  2. 查找渲染有问题的界面——GPU呈现模式分析(Profile GPU Rendering)
  3. View层级和绘制时间测定——Hierarchy Viewer
  4. 定位到帧(Frame)——Systrace
  5. 定位到方法耗时——Traceview

上面的五种工具或说是方法是有交叉功能的,想要做好UI耗时检测就要组合使用。下面是我的个人使用理解
1作为宏观的依据,我们在平时的布局时候应尽量减少层级同时选择合适的布局控件;
2也是作为宏观的依据,查找存在渲染有问题的页面;
3由于在AndroidStudio3.0之后启动Android Device Monitor很不方便。我认为可以用Layout Inspector来进行VIew层级的观测;
4和5组合使用或者直接使用5;

总结:1提供布局准则;2,3宏观观测;4,5细微纠错

下面是上面五种检测方法具体说明。

四.布局层级检测——GPU过度绘制

我们可以用Android系统中自带的工具来检测过度绘制。首先要保证系统版本在Android 4.1以上,接着在开发者选项中打开调试GPU过度绘制选项就可以进入GPU过度绘制模式,选择显示过度绘制区域,如下图所示
这里写图片描述
图中颜色的含义:

原色:没有过度绘制
蓝色:1 次过度绘制
绿色:2 次过度绘制
粉色:3 次过度绘制
红色:4 次及以上过度绘制

五.查找渲染有问题的界面——GPU呈现模式分析(Profile GPU Rendering)

是Android 4.1系统提供的开发辅助功能,我们可以在开发者选项中打开这一功能。如下图
这里写图片描述
选择“在屏幕上显示为条形图”则显示如下图
这里写图片描述

解释一下各个颜色条形图的含义

1.橙色
橙色代表处理的时间,是CPU告诉GPU渲染一帧的地方,这是一个阻塞调用,因为CPU会一直等待GPU发出接到命令的回复,如果橙色柱状图很高,则表明GPU很繁忙
2.红色
红色代表执行的时间,这部分是Android进行2D渲染 Display List的时间。如果红色柱状图很高,可能是由重新提交了视图而导致的。还有复杂的自定义View也会导致红的柱状图变高。
3.蓝色
蓝色代表测量绘制的时间,也就是需要多长时间去创建和更新DisplayList。如果蓝色柱状图很高,可能是需要重新绘制,或者View的onDraw方法处理事情太多

补充

在Android6.0以后有跟多的显示信息添加进来,如下图所示:
这里写图片描述
下面是各个颜色的含义。(由于我用的测试机版本较低的原因这里就不贴图了)

1.Swap Buffers:表示处理的时间,和上面讲到的橙色一样。
2.Command Issue:表示执行的时间,和上面讲到的红色一样。
3.Sync & Upload:表示的是准备当前界面上有待绘制的图片所耗费的时间,为了减少该段区域的执行时间,我们可以减少屏幕上的图片数量或者是缩小图片的大小。
4.Draw:表示测量和绘制视图列表所需要的时间,和上面讲到的蓝色一样。
5.Measure/Layout:表示布局的onMeasure与onLayout所花费的时间,一旦时间过长,就需要仔细检查自己的布局是不是存在严重的性能问题。
6.Animation:表示计算执行动画所需要花费的时间,包含的动画有ObjectAnimator,ViewPropertyAnimator,Transition等。一旦这里的执行时间过长,就需要检查是不是使用了非官方的动画工具或者是检查动画执行的过程中是不是触发了读写操作等等。
7..Input Handling:表示系统处理输入事件所耗费的时间,粗略等于对事件处理方法所执行的时间。一旦执行时间过长,意味着在处理用户的输入事件的地方执行了复杂的操作。
8.Misc Time/Vsync Delay:表示在主线程执行了太多的任务,导致UI渲染跟不上VSYNC的信号而出现掉帧的情况。

GPU呈现模式分析可以找到渲染有问题的界面,但是想要修复的话,只依赖它是不够的。我们要借助另一个工具Hierarchy Viewer,查看布局层次和每个View所花的时间

六.View层级和绘制时间测定——Hierarchy Viewer

6.1小插曲Android Studio 3.0开始弃用Android Device Monitor

由于我使用的是Androidstudio3.1.3版本,所以不能像3.0以前那样通过tool–>Android–>Android Device Monitor来启动Android Device Monitor了。因为Android Studio 3.0开始弃用Android Device Monitor。虽然我们不能从AndroidStudio中找到启动Android Device Monitor的按钮,我们还是可以通过其他方法来启动Android Device Monitor的,如下:

从sdk中启动Android Device Monitor:

1.先找到AndroidStudio配置的SDK路径(例如我的是C:\Users\gongxiaoou\AppData\Local\Android\sdk);
2.在tools文件夹中找到monitor.bat这个批处理文件,双击它启动我们的Android Device Monitor要等个几秒钟才能起来);

Hierarchy Viewer的替代品

从官方文档中我们可以看到Android Device Monitor 的很多功能在Androidstudio3.0之后都有相应的新工具来替代,下面是截图,这里附上链接Android Device Moitor3.0之后的变动,下面倒数第二个条目就是了。如果想看View的层级关系就用Tools–>Layout Inspector;如果想查看View的渲染速度就用android.view.Window.OnFrameMetricsAvailableListener(我感觉变得复杂了,还要自己来手写)。
这里写图片描述

6.2Android Device Monitor中Hierarchy Viewer中的使用

Hierarchy Viewer是Android SDK自带的可视化的调试工具,用来检查布局嵌套和绘制的时间。需要注意的是在在Android的官方文档中提到:出于安全考虑,Hierarchy Viewer只能连接Android开发版手机或是模拟器。
如何打开Hierarchy Viewer
上面我们已经启动了Android Device Monitor了,现在打开Hierarchy Viewer。如下图
这里写图片描述
上图中点击右上角DDMS左边的格子按钮(图中4处)弹出图中弹窗选中Hierarchy View选项(图中3处)另外两处含义(图中1处:设备名称;图中2处:包名全称),然后点击ok,之后如下图
这里写图片描述

View的层级关系

Hierarchy Viewer中有4个四个子窗口:
Windows:当前设备所有界面列表。
Tree View:将当前Activity的所有View的层次按照高层到低层从左到右显示出来。
Tree Overview:全局概览,以缩略的形式显示。
Layout View:整体布局图,以手机屏幕上真实的位置呈现出来。单击某一个控件,会在Tree Overview窗口中显示出对应的控件。
从上面的四个子窗口可以清除看出View的层级关系。下面我们看一下绘制时间。

绘制时间(含measure,layout,draw)

先选中要测试的控件,然后我们可以看到它的上面显示了Measure,Layout,Draw的时间为n/a,如下图
这里写图片描述
然后我们点击如下图中标记的按钮
这里写图片描述
从图中可以看出,被选中的LinearLayout给出了自身Measure、Layout和Draw的耗时,并且它所包含的View中都有了三个指示灯,分别代表当前View在Measure、Layout和Draw的耗时,绿色代表比其他50%View的同阶段(比如Measure阶段)速度要快,黄色则代表比其他50%View同阶段速度要慢,红色则代表比其他View同阶段都要慢,则需要注意了。如果想要看View的具体耗时,则点击该View就可以了。
说实话,个人觉得绘制时间功能好用,因为相比下面的手工写;但是查看View层级功能比较麻烦,因为要缩放工具里面的子窗口才能看的清楚需要的View。

6.3Layout Inspector的使用

首先打开要测试的app,然后打开Layout Inspector工具(Tools–>Layout Inspector)。如下图所示:
这里写图片描述
从图中可以清楚看到View各个层级的关系。

定位卡顿的真正原因Systrace和Traceview结合

在上面6.1中我们说了,在AndroidStudio3.0之后好多的检测工具都有所变化。我们参看里面关于Systrace和Traceview的描述如下:
Traceview

If you want to inspect existing .trace files, or ones you've captured by instrumenting your app with the Debug class, keep using Traceview.

If you want to record new method traces and inspect realtime CPU usage of your app's processes, use Android Studio's CPU profiler.

关于上面英文的理解:
第一句:如果有 Debug 类捕获的.trace文件了或是在App中做了埋点处理得到了 Debug 类捕获的.trace文件,那么用Traceview工具。
第二句:实时观察和监测app在运行时候CPU的使用情况及方法的执行情况用Cpu profiler工具。
Systrace

If you need to inspect native system processes and address UI jank caused by dropped frames, use systrace from the command line.

Otherwise, use Android Studio's CPU profiler to profile your app's processes.

关于上面英文的理解:
第一句:监测系统的进程和由丢帧引起的闪烁用Systrace 工具。
第二句:其他都用Cpu profiler工具。

总结有.trace文件的用Traceview工具;系统进程监测和由于丢帧引发的屏幕闪烁用Systrace工具;其他都用CPU profiler工具。

八.Systrace

8.1Systrace用来做什么的?

Systrace精确到Frame(帧)的粒度,能粗略分析定位,但不能找出引起CPU满载的真正原因,想找到真正原因借助Traceview

Systrace的背景
Systrace在Android4.1的时候新增,用来进行性能数据采样和分析。主要收集Android关键子系统如SufaceFlinger,WindowManagerService等FrameWork部分关键模块,服务,View体系系统等的运行信息。
Systrace包括跟踪系统的I/O操作,内核工作队列,CPU负载以及Android各个子系统的运行情况。
对于UI显示性能,比如动画播放卡顿,渲染卡顿等问题提供了分析依据。
Systrace跟踪的设备要在Android4.1版本以上,对于Android4.3版本之前和4.3版本之后使用上有点区别,现在也很少有人用Android4.3之前的版本,因此这里只讲Android4.3版本的使用方法。

8.2三种使用方式:

1.在DDMS上使用;
2.使用命令行来使用;
3.在代码中进行跟踪。
下面我们介绍第一种和第三种。(因为第一种为界面操作简单易用相对于第二种;第三种可以在具体的代码片段之间操作精确定位)

DDMS中使用
  1. 先运行自己要测试的app然后打开Android Device Monitor(上面有打开方式)找到DDMS按钮
  2. 点击Systrace按钮(下图中1处)进入抓取设置界面,如下图所示,可以设置跟踪的时间,以及trace文件输出的地址等内容。如下图所示。
  3. 设置完成后,我们就来操作的跟踪的过程。跟踪时间结束后,生成trace.html文件。
  4. 用Chrome打开trace.html文件进行分析。分析的方法,下面一起说。

这里写图片描述

代码中使用Systrace

Systrace并不会追踪应用的所有工作,在Android4.3及以上版本的代码中,可以使用Trace类对应用中的具体活动(代码片段)进行追踪。
Android源码中也引用了Trace类,比如RecyclerView中(TraceCompat类对Trace类进行了封装,只会在Android4.3及以上版本才会使用Trace类)。beginSection方法和endSection方法之间的代码会被追踪,因此要保证beginSection方法和endSection方法的调用成对出现,我们从下面可以看出。

private void consumePendingUpdateOperations() {
        ---代码省略---
        if (mDataSetHasChangedAfterLayout) {
            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
            dispatchLayout();
            TraceCompat.endSection();
            return;
        }
        ---代码省略---
        if (mAdapterHelper.hasAnyUpdateTypes(UpdateOp.UPDATE) && !mAdapterHelper
                .hasAnyUpdateTypes(UpdateOp.ADD | UpdateOp.REMOVE | UpdateOp.MOVE)) {
            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
            eatRequestLayout();
            mAdapterHelper.preProcess();
            if (!mLayoutRequestEaten) {
                if (hasUpdatedView()) {
                    dispatchLayout();
                } else {
                    // no need to layout, clean state
                    mAdapterHelper.consumePostponedUpdates();
                }
            }
            resumeRequestLayout(true);
            TraceCompat.endSection();
        } else if (mAdapterHelper.hasPendingUpdates()) {
            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
            dispatchLayout();
            TraceCompat.endSection();
        }
    }

8.3用Chrome分析Systrace

我们将刚刚使用DDMS的生成的trace.html文件分析。打开后效果如下图所示。
这里写图片描述
我们可以使用W键和S键进行放大和缩小,A键和D键进行左右移动。下面我们来看如何分析,先看各个参数区代表的含义。

1.Alerts区域

我们点击Alerts右侧的灰色原点会变成黄色,此时最下方展示如下图
这里写图片描述
这个Alert指出了View在Measure/Layout时耗费了大量的时间,导致出现jank(同一帧画了多次)。给出的建议是避免在动画播放期间控制布局。

2.CPU区域

接下来我们来查看CPU区域,每一行代表一个CPU核心和它执行任务的时间片,放大后会看到每个色块代表一个执行的进程,色块的长度代表其执行时间,如下图所示。
这里写图片描述
点击相应的颜色片段都会在下面展示具体的信息。
这里写图片描述
从中我们可以看到:

  1. 所执行的进程
  2. 执行的Thread
  3. 开始的时间
  4. 持续的时间
3. 应用区域

左上角显示应用id,这里面含有应用的帧数,Systrace会给出应用中的Frames分析,每一帧就是一个F圆圈,F圆圈有三种颜色,其中绿色表示Frame渲染流畅,黄色和红色则代表渲染时间超过了16.6ms,其中红的更严重些。
这里写图片描述
我们点击红色F圆圈,会给出该Frame的信息,如下图所示,点击选中的会变成黄色,其余都变成了灰色。
这里写图片描述

从图中可以看出,Frame给出了问题提示:Scheduling delay(调度延迟),当一帧绘制时间超过19ms会触发该提示,更何况这一帧已经有44ms,竟然sleep了将近39ms。按m键来高亮该时间段,我们来查看CPU的情况,如下图所示。
这里写图片描述
这里我们只能看出至于具体是什么让CPU繁忙,则需要使用Traceview来进行分析。

4.Alerts总体分析

点开最右边的Alerts按钮会给出Alert的总体分析,如下图所示。
这里写图片描述
Alerts会给出Alert类型,以及出现的次数。有了这些总体的分析,方便开发者对该时间段的绘制性能有一个整体的大概了解,便于进行下一步分析。

8.4Systrace总结

由于Systrace 是以系统的角度返回一些信息,只能为我们提供一个概览,它的深度是有限的,我们可以用它来进行粗略的检查,以便了解大概的情况,但是如果要分析更详细的,比如要找到是什么让CPU繁忙,某些方法的调用次数等,则还要借助另一个工具:Traceview。

九.Traceview

9.1Traceview简介

TraceView是Android SDK中自带的数据采集和分析工具。一般来说,通过TraceView我们可以得到以下两种数据:
单次执行耗时的方法。
执行次数多的方法。

9.2使用Traceview

要分析Traceview,则首先要得到一个trace文件,trace文件的获取有两种方式:
1.在DDMS中使用
2.在代码中加入调试语句,下面分别对这两种方式进行介绍。

9.2.1DDMS中使用

1.先打开Android Device Monitor,使用上面的方法。
2.选择相应的进程,并单击Start Method Profiling按钮。
3.对应用中需要监控的点进行操作。
4.单击Stop Method Profiling按钮,会自动跳到TraceView视图。

9.2.2代码中加入调试语句

如果开发中出现不好复现的问题,则需要在代码中添加TraceView监控语句。在开始监控的地方调用startMethodTracing方法,在需要结束监控的地方调用stopMethodTracing方法。系统会在SD卡中生成.trace文件,然后使用Androidstudio查看即可。

第一步:写一个模拟耗时操作的demo

为了生成.trace文件,并模仿一个耗时操作,这里我写一个很简单的demo,如下

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Debug.startMethodTracing("testg");//1
        initView();
    }
    private void initView() {
        try {
            //模拟一个不正常的操作,在初始化view的时候让线程睡上6s
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Override
    protected void onStop() {
        super.onStop();
        Debug.stopMethodTracing();
    }
}
第二步:运行这个工程,运行起来之后退出(为了onStop调用)。
第三步:使用Androidstudio中的Device File Explorer工具(在Androidstudio的右下面)找到生成的.trace文件。

在这里如果没有Device File Explorer,则通过View–>Tool Windows–>Device File Explorer找到它并点击添加到工具栏中。
我的.trace文件如下图
这里写图片描述

第四步:打开生成的.trace文件。

这里写图片描述
解释一下图中标注处的含义:

  1. 线程我们从这里可以选择查看各个线程的运行情况
  2. 下面x轴表示的时间(有Wall Clock Time和Thread Time两选项)
  3. 调用方法的名称
  4. 本方法调用的次数
  5. 本方法调用的总时间(自身和里面调用其他方法的时间和)
  6. 本方法单独执行的时间(出去里面调用其他方法的时间)

从这里我们可以很清晰的看到各个方法的调用情况,从而可以找出

  1. 哪个方法被重复调用造成了耗时
  2. 哪个方法自身的执行本身就是耗时操作

十.总结:

检测的方法选择在上面的三中已经阐述了。下一篇是关于从检测结果中总结平时开发中应该注意什么,也就是怎么写代码更合理。

猜你喜欢

转载自blog.csdn.net/gongxiaoou/article/details/81366929