上一篇流畅度概念向你们详细地描述了VSync机制和Choreographer编舞者的用法。可能所讲解的内容偏向理论概念,所以这篇是流畅度优化实操,整篇主要分三层,UI层、代码逻辑层、IO层来说述各个优化点,其中还会穿插多个辅助检测插件。可谓干货满满,但愿对你们有用。html
在App出现卡顿的时候,咱们第一时间会想到咱们的App是否是存在过分绘制的问题。为何要先看过分绘制的问题,由于直接直观方便啊,在每一台手机的开发者选项里中打开显示过分绘制区域,经过颜色咱们就能辨别咱们的App是否是存在过分绘制的问题。 可能存在一部分的测试甚至开发的同窗不知道什么是过分绘制?过分绘制指的是在屏幕一个像素上绘制屡次(超过一次),例如一个有背景的TextView,那显示文字的那个像素至少绘制了两次,一次是文字,一次是背景。 过分绘制显示的各类颜色所示含义以下:android
Overdraw倍数 | 像素点绘制次数 | 可接受区域 | |
---|---|---|---|
无色 | 0X | 1 | 全屏 |
蓝色 | 1X | 2 | 大部分 |
绿色 | 2X | 3 | 局部 |
淡红色 | 3X | 4 | 小部分 |
深红色 | 4X | ≥5 | 无 |
如今你们能够看一下本身的项目,找一个你认为布局稍微复杂的界面,而后在开发者选项打开显示过分绘制区域浏览器
经过颜色的判断,咱们检查对应的布局代码来优化过分绘制问题。性能优化
针对上面咱们看到的过分绘制的区域,咱们要想想应该怎么去优化,但这个时候咱们并不太清楚这个过分绘制的区域是怎么造成的,因此咱们要借助另一个工具Tracer for OpenGL ES,它能够记录和分析app每一帧的绘制过程,以及列出全部用到OpenGL ES的绘制函数和耗时,因此经过Tracer for OpenGL ES咱们能够很容易的看出app的每一帧是怎么画出来的。bash
简要使用步骤:网络
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<Button
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@android:color/background_dark"
android:text="Button"
android:textColor="@android:color/white" />
</android.support.design.widget.CoordinatorLayout>
复制代码
经过刚刚的演示,咱们就能够用到了Tracer for OpenGL ES来查找过分绘制的地方来。app
小插曲:打开本身mac的AndroidStudio3.1时,居然一时找不到DDMS,查阅资料发现DDMS在AndroidStudio3.1已经不推荐使用了,只能用回公司AndroidStudio3.0截图,这里为之后的优化工具文章留下一个伏笔。异步
接着咱们介绍一下Hierarchy Viewer,经过它咱们能够查找布局不合理的地方,Hierarchy Viewer的使用方法较为简单,AndroidStudio中,一样经过Android Device Monitor,接着Window-> Open Perspective -> Hierarchy Viewer。经过Hierarchy Viewer能够看到咱们打开的Activity的UI Tree状况。(注意:咱们用模拟器做为例子,先用模拟器运行打开你的应用,再打开Hierarchy Viewer面板。)ide
拿到UI Tree以后,咱们主要分析如下三个问题:(我用一个简单的Demo来分析)函数
【问题1】没有用的父布局
使用Hierarchy Viewer查看咱们的UI Tree,如发现红框的RelativeLayout是CustomTestView惟一子View,咱们能够看看是否能把RelativeLayout子View又放到CustomTestView里,这样就能够把RelativeLayout这一层去掉,经过查看代码,咱们发现其实RelativeLayout这一层是多余的,咱们直接经过merge标签把RelativeLayout和CustomTestView合并.(这种状况在自定义View很是常见)
【问题2】某种状况才会使用的UI被设置成View.GONE 咱们在开发应用程序的时候,常常会遇到这种状况,会在运行时动态根据条件来决定显示哪个View或者ViewGroup,把最早要显示的放在第一位显示,不是第一时间要显示的暂时设置为View.GONE。这样的作法优势是逻辑简单,并且控制起来很是的方便,可是缺点是会消耗资源,虽然把View或者ViewGroup的初始可见设为View.GONE,可是在Inflate布局的时候,View仍是被Inflate,也就是说仍然会建立对象,会被实例化,会被设置属性,也就是说会消耗内存等资源的。官方推荐的作法是使用ViewStub,ViewStub是一个轻量级的View,他是一个使用资源很是小的控件。(若是不明白设置成GONE,仍然会消耗资源的同窗,能够经过关于View的建立与ViewStub的源码分析进行理解)在咱们的代码中,错误页面ErrorView常常会出现这种状况。
【问题3】使用LinearLayout排版致使布局层次加深 从下图能够发现,下面布局是用两个LinearLayout嵌套实现的,可是经过使用一个RelativeLayout咱们能够实现一样的效果,这样就能够减小一个层次,从图中能够很明显的看出优化效果。
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/app_status_bar_bg_color</item>
<item name="colorPrimaryDark">@color/app_status_bar_bg_color</item>
<item name="android:textColorPrimary">@android:color/black</item>
<item name="android:windowBackground">@color/app_frame_bg_color</item>
</style>
复制代码
可是在布局页面,设计人员设计的底色,根本不是默认的背景色,若是咱们在这个页面的根布局再设一个背景的话就是多绘制一层背景。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"><!-- 会致使过分绘制的写法 -->
***
</RelativeLayout>
复制代码
这种状况,咱们能够这样处理:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setBackgroundDrawableResource(android.R.color.white);
setContentView(R.layout.activity_main);
***
}
复制代码
这样修改布局的背景色,咱们能够避免出现过分绘制的状况。另外上面的设置背景代码,要注意书写顺序,这里可包含了很多View的建立的知识,有兴趣的同窗能够自行查阅。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:divider="@drawable/divider_horizontal_w7_transparent"
android:orientation="horizontal"
android:showDividers="middle">
***
</LinearLayout>
复制代码
mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
});
复制代码
强烈不建议,直接在itemView中直接画分割线,虽然是简单,可是咱们是一个有追求的开发者,尽可能把代码写得漂亮一点。
这里帮你们整理几个比较经典的注意点,因为有不一样层次的读者,因此这里不用具体代码来说解,若是有不理解的同窗,能够单独对某个点进行查阅。
在Android Lint:Performance这个错误节点下,很是清晰地描述了你都有哪些错误,每个错误都有很是清晰的描述,你应该如何去改,在右边的箭头,程序帮咱们直接定位到错误代码地方,是否是很是方便!
通过上述的分析调整后,咱们接着分析一下关于代码逻辑层的优化。
Traceview是Android设备的一个很是好用的性能分析工具,它能够经过详细的界面,让咱们跟踪程序的性能,而且能清晰地查看到每个函数的耗时和调用次数,因此咱们用Traceview的时候要主要两种影响流畅度的缘由。一:主线程占用cpu时间很长的方法函数;二:线程调用的次数
我经过具体的应用来具体分析,好比说商城类型的首页,经过是使用RecyclerView,那么咱们能够先推断影响RecyclerView的流畅度大多数是RecyclerView.Adapter#onBindViewHolder的方法。
一样是经过Android Device Monitor面板,在下图左方选中须要分析的应用,再点击左上角按钮,当你以为数据收集足够时,再次点击那个按钮便可,这时Traceview会自动打开trace文件。
那么经过Traceview面板的上部分为时间线面板,左上方面板显示的是采集数据中所采集的线程信息,右边上方面板为时间线,时间线上,每一条线程在采集时间段内所涉及的函数调用信息。而下部分为函数分析面板,是traceview核心界面,它所提供的信息数据很是多,他主要展现了某条线程中各个函数方法调用的状况,包括cpu使用时间,函数方法调用次数,和函数方法真实执行时间等信息,这些信息就是咱们分析流畅度的关键所在。
咱们了解一下操做,获取方法的调用顺序:
Profile Panel各列功能描述说明
列名 | 描述 |
---|---|
Name | 调用的函数方法名 |
Incl Cpu Time | 函数占用的CPU时间,包含内部调用其它函数的CPU时间 |
Excl Cpu Time | 函数占用的CPU时间,但不含内部调用其它函数所占用的CPU时间 |
Incl Real Time | 函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间 |
Excl Real Time | 函数运行的真实时间(以毫秒为单位),不含调用其它函数所占用的真实时间 |
Call+Recur Calls/Total | 函数被调用次数、递归调用占总调用次数的百分比 |
Cpu Time/Call | 函数调用CPU时间与调用次数的比,至关于该函数平均执行时间 |
Real Time/Call | 函数调用CPU时间与调用次数的比,至关于该函数平均执行时间,这个时间包含来内部调用的其余函数的执行时间 |
看回上图,我经过搜索RecyclerView.Adapter#onBindViewHolder中调用的抽象方法inflateFromModel,找到了首页某一个ViewHolder,从这个ViewHolder#inflateFromModel方法中,找到它调用了两个方法,一个是图片显示的方法,另外一个是正则判断的方法,因为ViewHolder#inflateFromModel在滑动机制中会不断地调用,而这个正则判断的目的是对点击事件中的控件进行setTag操做的值进行脏数据验证,其实这个正则判断其实没有必要在这里执行,那么咱们能够不做任何判断,将拿到的值直接View#setTag,而后在对应的onClick()再进行正则判断,这样就能够减小那些没必要要占用主线程cpu时间的方法函数,达到提升流畅度的效果。
Systrace很是直观地展现每一个线程上面的API的调用顺序和耗时状况。一样是经过Android Device Monitor面板,下图中的箭头,建议跟踪持续时间不要太长,为了更好地定位问题.接着生成trace.html文件,经过Google Chrome浏览器打开。
先了解一下几个经常使用的快捷键:
操做 | 做用 |
---|---|
w | 放大 |
s | 缩小 |
a | 左移 |
d | 右移 |
m | 标记当前选定区域 |
/ | 搜索关键字 |
下拉trace.html咱们能够看到frame,每一帧就显示为圆圈,正常绘制是1秒60帧,大约一帧16.6毫秒,在这个值如下是正常颜色绿色,若是超过它就会变成红色、黄色。非绿色的都说明有问题。这时须要经过’w’键放大那一帧,而后按‘m’键高亮,进一步分析问题。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Trace.beginSection("Activity_onCreate");
***
Trace.endSection();
}
复制代码
总体来讲,Systrace展示的信息是不少的,可是如何加以利用还得继续研究,对应上面涉及代码定位看上去就像一个Log,监测开始与结束时间罢了,感受有点鸡肋。
IO可分红为网络请求和磁盘读写IO,相信你们都知道,MVP模式下的Model层也是对IO层进行操做。而在主线程中进行长时间和频繁的IO操做,对流畅度是有很是大的影响的,对于网络请求在安卓4.0以后,就已经不能在主线程进行网络操做了,不然程序会出现crash,所以咱们对IO层的操做要进行监控。而Android为咱们提供了StrictMode方式来监控代码是否出现上述的状况。
StrictMode主要有两种策略,一是线程方面策略(TreadPolicy),二是VM方面策略(VmPolicy).
只要主线程中配置了并启动,它就能监听主线程的运行状况,当发现有重大问题时和违背策略的时候,就会以logcat的形式提示用户。
最后我来总结一下通篇对流畅度优化上的经验:
2.RecyclerView性能优化
4.第三方平台
写在结尾:我在这篇博客的时候,刚刚出现了AndroidStudio3.2金丝雀版本,而部分上述的工具,Google已经再也不推荐使用,接下来我会继续更新Google新推荐的优化工具文章,努力成为一个性能优化的好手。