Android绘制优化(一)绘制性能分析

前言

一个优秀的应用不只仅是要有吸引人的功能和交互,同时在性能上也有很高的要求。运行Android系统的手机,虽然配置在不断的提高,但仍旧没法和PC相比,没法作到PC那样拥有超大的内存以及高性能的CPU,所以在开发Android应用程序时也不可能无限制的使用CPU和内存,若是对CPU和内存使用不当也会形成应用的卡顿和内存溢出等问题。所以,应用的性能优化对于开发人员有着更高的要求。Android性能优化分为不少种,比较经常使用的有绘制优化、内存优化、耗电优化和稳定性优化等,这个系列咱们就来学习性能优化中的绘制优化。javascript

1.绘制原理

Android绘制View有三个主要的步骤,分别是measure、layout和draw。关于它们的原理请查看个人文章Android View体系(七)从源码解析View的measure流程Android View体系(八)从源码解析View的layout和draw流程,这里就不在赘述。measure、layout和draw方法主要是运行在系统的应用框架层,而真正将数据渲染到屏幕上的则是系统Nativie层的SurfaceFlinger服务来完成的。html

绘制过程主要是由CPU 来进行Measure、Layout、Record、Execute的数据计算工做,GPU负责栅格化、渲染。CPU和GPU是经过图形驱动层来进行链接的。图形驱动层维护了一个队列,CPU将display list添加到该队列中,这样GPU就能够从这个队列中取出数据进行绘制。java

渲染时间线
FPS(Frames Per Second)这个名词我想不少同窗都知道,它是指画面每秒传输帧数,通俗来说就是指动画或视频的画面数,最简单的举例就是咱们玩游戏时,若是画面在60fps则不会感受到卡顿,若是低于60fps,好比50fps则会感受到卡顿,你就能够考虑要换显卡或者采起其余一些措施了。
要想画面保持在60fps,则须要每一个绘制时长在16ms之内,以下图所示。python

Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染, 若是每次渲染都成功,这样就可以达到流畅的画面所须要的60fps,那什么是VSYNC呢?VSYNC是Vertical Synchronization(垂直同步)的缩写,是一种定时中断,一旦收到VSYNC信号,CPU就开始处理各帧数据。
若是某个操做要花费24ms,这样系统在获得VSYNC信号时没法进行正常的渲染,会发生丢帧。用户会在32ms中看到同一帧的画面,以下图所示。android

产生卡顿缘由有不少,主要有如下几点:性能优化

  • 布局Layout过于复杂,没法在16ms内完成渲染。
  • 同一时间动画执行的次数过多,致使CPU或GPU负载太重。
  • View过分绘制,致使某些像素在同一帧时间内被绘制屡次。
  • UI线程中作了稍微耗时的操做。

为了解决上述的问题,除了咱们要在写代码时要注意外,也能够借助一些工具来分析和解决卡顿问题。微信

2.Profile GPU Rendering

Profile GPU Rendering是Android 4.1系统提供的开发辅助功能,咱们能够在开发者选项中打开这一功能,以下图所示。框架


咱们点击Profile GPU Rendering选项并选择On screen as bars即开启Profile GPU Rendering功能。接着屏幕会显示出彩色的柱状图,以下所示。


上面的彩色的图的横轴表明时间,纵轴表示某一帧的耗时。绿色的横线为警惕线,超过这条线则意味着时长超过了16m,尽可能要保证垂直的彩色柱状图保持在绿线下面。这些垂直的彩色柱状图表明着一帧,不一样颜色的彩色柱状图表明不一样的含义:

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

在Android 6.0中,有更多的颜色被加了进来,以下图所示:
ide

下面来分别介绍它们的含义:函数

  • Swap Buffers:表示处理的时间,和上面讲到的橙色同样。
  • Command Issue:表示执行的时间,和上面讲到的红色同样。
  • Sync & Upload:表示的是准备当前界面上有待绘制的图片所耗费的时间,为了减小该段区域的执行时间,咱们能够减小屏幕上的图片数量或者是缩小图片的大小。
  • 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所花的时间,这个工具会在下一篇文章进行介绍。

3.Systrace

Systrace是Android4.1中新增的性能数据采样和分析工具。它可帮助开发者收集Android关键子系统(SurfaceFlinger、WindowManagerService等Framework部分关键模块、服务,View体系系统等)的运行信息。Systrace的功能包括跟踪系统的I/O操做、内核工做队列、CPU负载以及Android各个子系统的运行情况等。对于UI显示性能,好比动画播放不流畅、渲染卡顿等问题提供了分析数据。
使用Systrace
Systrace跟踪的设备要在Android4.1版本以上,对于Android4.3版本以前和4.3版本以后使用上有点区别,如今也不多有人用Android4.3以前的版本,所以这里只讲Android4.3版本的使用方法。Systrace能够在DDMS上使用,可使用命令行来使用,也能够在代码中进行跟踪。接下来分别来介绍这三种方式。
在DDMS中使用Systrace
1.首先咱们要打开Android Studio的Tool中的Android Device Monitor,并链接手机。
2.点击Systrace按钮进入抓取设置界面,以下图所示。


抓取设置界面能够设置跟踪的时间,以及trace文件输出的地址等内容。以下图所示。

3.设置完成后,咱们就来操做的跟踪的过程。跟踪时间结束后,生成trace.html文件。
4.用Chrome打开trace.html文件进行分析。分析的方法,后文会讲到。

用命令行使用Systrace
Android 提供一个python脚本文件 systrace.py,它位于Android SDK 目录 /tools/systrace 中,咱们能够执行如下命令来使用Systrace:

$ cd android-sdk/platform-tools/systrace
$ python systrace.py --time=10 -o newtrace.html sched gfx view wm复制代码

在代码中使用Systrace
Systrace并不会追踪应用的全部工做,在Android4.3及以上版本的代码中,可使用Trace类对应用中的具体活动进行追踪。
Android源码中也引用了Trace类,好比RecyclerView:

...
 private final Runnable mUpdateChildViewsRunnable = new Runnable() {
        public void run() {
            if (!mFirstLayoutComplete) {
                return;
            }
            if (mDataSetHasChangedAfterLayout) {
                TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
                dispatchLayout();
                TraceCompat.endSection();
            } else if (mAdapterHelper.hasPendingUpdates()) {
                TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
                eatRequestLayout();
                mAdapterHelper.preProcess();
                if (!mLayoutRequestEaten) {
                    rebindUpdatedViewHolders();
                }
                resumeRequestLayout(true);
                TraceCompat.endSection();
            }
        }
    };
    ...复制代码

TraceCompat类对Trace类进行了封装,只会在Android4.3及以上版本才会使用Trace类,其中beginSection方法和endSection方法之间的代码会被追踪,endSection方法会只会结束最近的beginSection方法,所以要保证beginSection方法和endSection方法的调用次数要相同。

用Chrome分析Systrace
经过前面的方法生成的trace.html须要用Chrome打开,打开后效果以下图所示。

咱们可使用W键和S键进行放大和缩小,A键和D键进行左右移动。
Alert区域
首先来看Alert区域,这一区域会标记处性能有问题的点,单击叹号图标就能够查看某一个Alert的问题描述,以下所示。


这个Alert指出了View在Measure/Layout时耗费了大量的时间,致使出现jank(同一帧画了屡次)。给出的建议是避免在动画播放期间控制布局。

CPU区域
接下来咱们来查看CPU区域,每一行表明一个CPU核心和它执行任务的时间片,放大后会看到每一个色块表明一个执行的进程,色块的长度表明其执行时间,以下图所示。

图中CPU 0主要执行adbb线程和InputReader线程,CPU 2主要执行了surfaceflinger线程和ordinatorlayout进程中的RenderThread线程,咱们点击RenderThread色块,会给出RenderThread的相关信息,以下图所示。

图中给出了当前色块所运行的线程和进程、开启时间和持续时间等信息。

应用区域
应用区域会显示应用的帧数,以下图所示。

Systrace会给出应用中的Frames分析,每一帧就是一个F圆圈,F圆圈有三种颜色,其中绿色表示Frame渲染流畅,黄色和红色则表明渲染时间超过了16.6ms,其中红的更严重些。咱们点击红色F圆圈,会给出该Frame的信息,以下图所示。

从图中能够看出,Frame给出了问题提示:Scheduling delay(调度延迟),当一帧绘制时间超过19ms会触发该提示,更况且这一帧已经有将近40ms了。致使这一问题产生的缘由主要是线程在绘制时,在很长一段时间都没有分配到CPU时间片,所以没法继续进行绘制。按m键来高亮该时间段,咱们来查看CPU的状况,以下图所示。

能够看出这个时间段中两个CPU都在满负荷运行。至于具体是什么让CPU繁忙,则须要使用Traceview来进行分析。

Alerts整体分析
点开最右边的Alerts按钮会给出Alert的整体分析,以下图所示。

Alerts会给出Alert类型,以及出现的次数。有了这些整体的分析,方便开发者对该时间段的绘制性能有一个总体的大概了解,便于进行下一步分析。
因为Systrace 是以系统的角度返回一些信息,只能为咱们提供一个概览,它的深度是有限的,咱们能够用它来进行粗略的检查,以便了解大概的状况,可是若是要分析更详细的,好比要找到是什么让CPU繁忙,某些方法的调用次数等,则还要借助另外一个工具:Traceview。

4.Traceview

TraceView是Android SDK中自带的数据采集和分析工具。通常来讲,经过TraceView咱们能够获得如下两种数据:

  • 单次执行耗时的方法。
  • 执行次数多的方法。

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

DDMS中使用
1.首先咱们要打开Android Studio的Tool中的Android Device Monitor,并链接手机。
2.选择相应的进程,并单击Start Method Profiling按钮。
3.对应用中须要监控的点进行操做。
4.单击Stop Method Profiling按钮,会自动跳到TraceView视图。

代码中加入调试语句
若是开发中出现很差复现的问题,则须要在代码中添加TraceView监控语句,代码以下所示。

Debug.startMethodTracing();
...
Debug.stopMethodTracing();复制代码

在开始监控的地方调用startMethodTracing方法,在须要结束监控的地方调用stopMethodTracing方法。系统会在SD卡中生成trace文件,将trace文件导出并用SDK中的Traceview打开便可。固然不要忘了在manifest中加入 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>权限。

分析Traceview
为了分析Traceview,咱们来举一个简单的例子来生成trace文件,这里采用第二种方式:代码中加入调试语句。代码以下所示。

public class CoordinatorLayoutActivity extends AppCompatActivity {
    private ViewPager mViewPager;
    private TabLayout mTabLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_layout);
        Debug.startMethodTracing("test");//1
        initView();
   ...
    }

    private void initView() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        Debug.stopMethodTracing();
    }
}复制代码

在注释1处调用了startMethodTracing方法开始监控,其中test是生成的trace文件的名称。在initView中咱们特地调用sleep方法来作耗时操做。在onStop方法中咱们调用了stopMethodTracing方法结束监控。这时会在SD卡根目录生成test.trace文件,咱们将该文件导出到桌面,用Traceview来分析test.trace文件,咱们在cmd中执行以下语句。

咱们进入traceview所在的目录(直接将traceview.bat拖入到cmd中),并执行上图的traceview语句后会弹出Traceview视图,它分为两部分,分别是时间片面板和分析面板,咱们先来看时间片面板,以下图所示。


其中x轴表明时间的消耗,单位为ms,y轴表明各个线程。通常会查看色块的长度,明显比较长的方法重点去关注,具体的分析还得看分析面板,以下图所示。

每一列数据的表明的含义以下表所示。

列名 含义
Name 该线程运行过程当中调用的函数名
Incl Cpu Time% 某个方法包括其内部调用的方法所占用CPU时间百分比
Excl Cpu Time% 某个方法不包括其内部调用的方法所占用CPU时间百分比
Incl Real Time% 某个方法包括其内部调用的方法所占用真实时间百分比
Excl Real Time% 某个方法不包括其内部调用的方法所占用真实时间百分比
Calls + Recur Calls / Total 某个方法次数+递归调用次数
Cpu Time / Call 该方法平均占用CPU时间
Cpu Time / Call 该方法平均占用真实时间
Incl Cpu Time 某个方法包括其内部调用的方法所占用CPU时间
Excl Cpu Time 某个方法不包括其内部调用的方法所占用CPU时间
Incl Real Time 某个方法包括其内部调用的方法所占用真实时间
Excl Real Time 某个方法不包括其内部调用的方法所占用真实时间

由于咱们用sleep方法来进行耗时操做,因此这里咱们能够单击Incl Real Time来进行降序排列。其中有不少系统调用的方法,咱们来进行一一过滤。最终咱们发现了CoordinatorLayoutActivity的initView方法Incl Real Time的时间为1000.493ms,这显然有问题,以下图所示。


从图中咱们能够看出是调用sleep方法致使的耗时。关于Traceview还有不少种分析状况,就须要你们在平时进行积累了。
好了关于绘制性能分析,就讲到这,若是以为不过瘾,本系列的后续文章还有大波的内容会持续向你砸来。

参考资料
《Android群英传 神兵利器》
《Android应用性能优化最佳实践》
blog.csdn.net/itachi85/ar…
www.cnblogs.com/sunzn/p/319…
blog.csdn.net/androiddeve…
www.tuicool.com/articles/jM…
www.mobile-open.com/2015/85005.…


欢迎关注个人微信公众号,第一时间得到博客更新提醒,以及更多成体系的Android相关原创技术干货。
扫一扫下方二维码或者长按识别二维码,便可关注。

相关文章
相关标签/搜索