}
这样修改布局的背景色,我们可以避免出现过度绘制的情况。另外上面的设置背景代码,要注意书写顺序,这里可包含了不少View的创建的知识,有兴趣的同学可以自行查阅。
- 减少写View与ViewGroup
- 可以使用RelativeLayout减少层级的就使用RelativeLayout,否则使用LinearLayout线性布局。因为Android中RelativeLayout的测量次数比LinearLayout(不含weight的情况下)多,可以了解一下关于RelativeLayout、LinearLayout、FrameLayout的ViewGroup的测量源码分析。
- 使用SpannableString。相信大家对SpannableString都非常熟悉了,这是一个优化减少书写View的利器。
- 优雅的给LinearLayout、RecyclerView设置分割线。
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中直接画分割线,虽然是简单,但是我们是一个有追求的开发者,尽量把代码写得漂亮一点。
- 使用merge标签、ViewStub标签、include标签。上面我们都有讲解过。
- drawableLeft 代替ImageView + TextView
- 使用ConstraintLayout。作为AndroidStudio新版本的推荐的默认布局,可想它的强大之处,它是RelativeLayout的加强版,它是百分比布局(已被Deprecated)的替换品。
- %1$d代替TextView + TextView。(如果需要多语言适配你就懂了这的重要性)
这里帮大家整理几个比较经典的注意点,由于有不同层次的读者,所以这里不用具体代码来讲解,如果有不理解的同学,可以单独对某个点进行查阅。
- 代码检测神器——Lint检测工具 估计有一部分同学看完上面的分析讲解之后会觉得,好麻烦呀,要打开这个然后又要那里弄一下。然后就放弃了。接下来这个真的非常适合这部分同学使用。 打开Lint的步骤:Analyze -> Inspect Code -> 选择你需要分析的目录,然后点击确定分析
在Android Lint:Performance这个错误节点下,非常清晰地描述了你都有哪些错误,每一个错误都有非常清晰的描述,你应该如何去改,在右边的箭头,程序帮我们直接定位到错误代码地方,是不是非常方便!
代码逻辑层优化
经过上述的分析调整后,我们接着分析一下关于代码逻辑层的优化。
- Traceview
Traceview是Android设备的一个非常好用的性能分析工具,它可以通过详细的界面,让我们跟踪程序的性能,并且能清晰地查看到每一个函数的耗时和调用次数,所以我们用Traceview的时候要主要两种影响流畅度的原因。一:主线程占用cpu时间很长的方法函数;二:线程调用的次数
我通过具体的应用来具体分析,比如说商城类型的首页,通过是使用RecyclerView,那么我们可以先推断影响RecyclerView的流畅度大多数是RecyclerView.Adapter#onBindViewHolder的方法。
同样是通过Android Device Monitor面板,在下图左方选中需要分析的应用,再点击左上角按钮,当你觉得数据收集足够时,再次点击那个按钮即可,这时Traceview会自动打开trace文件。
那么通过Traceview面板的上部分为时间线面板,左上方面板显示的是采集数据中所采集的线程信息,右边上方面板为时间线,时间线上,每一条线程在采集时间段内所涉及的函数调用信息。而下部分为函数分析面板,是traceview核心界面,它所提供的信息数据非常多,他主要展示了某条线程中各个函数方法调用的情况,包括cpu使用时间,函数方法调用次数,和函数方法真实执行时间等信息,这些信息就是我们分析流畅度的关键所在。
我们了解一下操作,获取方法的调用顺序:
- 在traceview中搜索响应的方法名
- 搜索出的方法会自动展开,其中包含Parents 和 Children 两组信息
- 点击Parents下的方法名,直接跳转到调用当前的方法处。Children则相反
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
Systrace非常直观地展示每个线程上面的API的调用顺序和耗时情况。同样是通过Android Device Monitor面板,下图中的箭头,建议跟踪持续时间不要太长,为了更好地定位问题.接着生成trace.html文件,通过Google Chrome浏览器打开。
先了解一下几个常用的快捷键:
操作 | 作用 |
---|---|
w | 放大 |
s | 缩小 |
a | 左移 |
d | 右移 |
m | 标记当前选定区域 |
/ | 搜索关键字 |
下拉trace.html我们可以看到frame,每一帧就显示为圆圈,正常绘制是1秒60帧,大约一帧16.6毫秒,在这个值以下是正常颜色绿色,如果超过它就会变成红色、黄色。非绿色的都说明有问题。这时需要通过’w’键放大那一帧,然后按‘m’键高亮,进一步分析问题。
Systrace能自动分析trace中的事件,并能自动高亮性能问题作为一个Alerts,我们可以根据提示进行分析优化。
但是,这里所标的问题,我们怎么能定位到具体哪一部分的代码呢?Systrace为我们提供了对应的API,然后在对应的持续时间。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Trace.beginSection(“Activity_onCreate”);
Trace.endSection();
}
整体来说,Systrace展现的信息是很多的,但是如何加以利用还得继续研究,对应上面涉及代码定位看上去就像一个Log,监测开始与结束时间罢了,感觉有点鸡肋。
App的IO层代码优化
IO可分成为网络请求和磁盘读写IO,相信大家都知道,MVP模式下的Model层也是对IO层进行操作。而在主线程中进行长时间和频繁的IO操作,对流畅度是有非常大的影响的,对于网络请求在安卓4.0之后,就已经不能在主线程进行网络操作了,否则程序会出现crash,因此我们对IO层的操作要进行监控。而Android为我们提供了StrictMode方式来监控代码是否出现上述的情况。
StrictMode主要有两种策略,一是线程方面策略(TreadPolicy),二是VM方面策略(VmPolicy).
- 线程策略主要用于检测UI线程中是否存在读写磁盘的操作,是否有网络请求操作,以及检查自定义代码是否在UI线程执行得比较慢的情况
- 自定义的耗时调用 使用detectCustomSlowCalls()开启
- 磁盘读取操作 使用detectDiskReads()开启
- 磁盘写入操作 使用detectDiskWrites()开启
- 网络操作 使用detectNetwork()开启
- VmPolicy策略主要用于发现内存问题,比如Activity内存泄漏,SQL对象内存泄漏,IO操作对象资源未释放。
- Activity泄露 使用detectActivityLeaks()开启
- 未关闭的Closable对象泄露 使用detectLeakedClosableObjects()开启
- 泄露的Sqlite对象 使用detectLeakedSqlLiteObjects()开启
- 检测实例数量 使用setClassInstanceLimit()开启
只要主线程中配置了并启动,它就能监听主线程的运行情况,当发现有重大问题时和违背策略的时候,就会以logcat的形式提示用户。
流畅度优化经验总结
最后我来总结一下通篇对流畅度优化上的经验:
- UI布局优化
- 使用LinearLayout代替RelativeLayout,因为LinearLayout性能上稍微好一点
- 如果复杂的布局,我们可以使用RelativeLayout来解决复杂的布局关系
- 尽量少用LinearLayout的layout_weight属性,因为它会消耗较大的性能
- 对应可以复用的布局使用include标签来进行复用
- 使用ViewStub标签来加载一些不是必定出现使用的布局
- 使用merge来减少不必要的层级嵌套
- 去除多余的背景颜色,减少过度绘制问题
- 使用compound drawables、%1$d 减少布局的创建
2.RecyclerView性能优化
- 在RecyclerView.Adapter#onBindViewHolder函数下的复用问题,注意哪些不必要的变量创建
- 异步加载图片
- 对于一些不必要的操作不要在滑动复用部分进行实现,这样会影响cpu运算
- UI主线程
- 异步请求网络数据
- 如果较为耗时的操作不要放在UI线程中实现
- 不要在UI线程外操作UI
4.第三方平台 - 腾讯开源工具——GT
- 听云——应用性能监控平台
写在结尾:我在这篇博客的时候,刚刚出现了AndroidStudio3.2金丝雀版本,而部分上述的工具,Google已经不再推荐使用,接下来我会继续更新Google新推荐的优化工具文章,努力成为一个性能优化的好手。
实现
- 不要在UI线程外操作UI
4.第三方平台 - 腾讯开源工具——GT
- 听云——应用性能监控平台
写在结尾:我在这篇博客的时候,刚刚出现了AndroidStudio3.2金丝雀版本,而部分上述的工具,Google已经不再推荐使用,接下来我会继续更新Google新推荐的优化工具文章,努力成为一个性能优化的好手。