Android性能优化学习与实践小结

Android性能优化学习与实践小结

前言

本文主要是本人这大约一周的时间在项目中进行性能优化的学习与实践笔记,对于性能优化方面有许多优秀的文章。文中大量引用了其他文章的描述,以及根据自己的理解整理成文,主要是主要从内存和流畅度两方面出发,对这部分工作的一个小结。老大一直强调性能优化方面的工作,但是一直没有做。这段产品正在重新规划,终于来做一做性能优化的工作。再次阅读了张总的Android应用开发进阶必经之路之性能优化,以及胡凯的Android性能优化典范,TMQ团队的《移动App性能评测与优化》,从中收获良多,在此真心感谢前辈们的分享。文中不足之处,望批评指正。

一、内存

内存,对于APP而言,是一项非常珍贵的资源,我们必须要合理的“借用”,为何说是借用 ?我想这是我自己的一点理解。古语有云:有借有还,再借不难,所以当你申请了内存,那就得记得适时释放出来。当然,如果,你如果又借给了别人,那么也要告诉别人要记得还,不然就很可能 泄漏了 。总之,内存是借来的,要记得还。

1、内存抖动

  • 原因:频繁的创建对象,并快速释放该对象,导致JVM频繁的GC;
  • 现象:AS的Monitor观察内存,明显的锯齿状,频繁的涨跌;通过Allocation Tracker 查看短时间内,同一个栈不断进出的相同对象。
  • 分析:
    • for循环中分配对象;
    • 自定义onDraw中创建对象;
  • 解决:尽量避免上述情况,若实在无法避免,可以考虑采用对象池模型来处理;

2、内存泄漏

  • 概念:程序中不再使用的对象无法被GC识别,一直占用内存空间无法被释放掉。机器内存剩余空间减少,一方面需要申请更多的内存浪费资源,另一方面频繁的GC引发的性能问题;
  • 常见问题
    • 自定义的View中set了listener等回调接口,没有主动释放;
    • 自定义的Layout中set进了外部的某些view,没有主动释放;
    • 初始化了第三方库,如volley,但是没有主动释放;
    • 从activity中将actvity中的对象set进了包含的fragment,fragment的持有了该对象,fragment在destroy时没有主动释放;
    • 非静态内部类导致的内存泄漏,如Hanlder,可以将内部类写成静态的,并使用弱引用的方式;
    • IO操作,使用完后记得关闭stream之类;
    • 自定义View中存在TypeArray,使用完之后没有关闭;
    • 四大组件的context;
  • 查找办法
    • AS的Memory Usage获取内存使用情况;
    • AS获取内存快照分析内存,找到没有释放的对象,分析修改,验证;
  • 如何避免

    • 减少内存使用
      • 轻量级数据结构,如HashMap
      • 减少Bitmap占用内存,缩放比例,修改解码格式;
      • 使用更小的图片
    • 内存重复使用
      • 系统自带的资源,如color、layout\string等
      • ListView,GridView等的itemView复用;
      • Bitmap对象复用;
      • StringBuilder字符串拼接;
  • 关键点:除了使用一些方法去检查修复,我们应该有个良好的内存管理意识,尽管JVM有自动的内存管理机制,但是由于我们书写了不合理的代码,到时无法GC。所以学会合理管理内存、节约资源也很重要。

二、流畅度

流畅度是用户体验中很重要的一部分,逼近用户的耐心很有限,就我个人而言。如果一个应用卡的要死,绝逼先卸载,也许永远不会再用啦。简单点说,就是应用在切换、渲染界面给人感觉舒不舒服,是不是有卡顿现象。我们通常通过FPS这一指标来评判,但是在《移动APP性能评测与优化》一书中提出FPS这一指标并不准备,所以提出了SM的概念。下面是流畅度优化的几个方面:

1、过度绘制

这个没有什么可说的,在系统设置中打开调试,就可以观察到现象。根据不同个颜色来判断是否过度绘制,网上的教程很多。出现过度绘制主要是由于View的backgroud重复绘制。首先Window本身就有自己的background,如果要自定义自己的background,得记得把window的background设为null。

2、布局优化

  • 层级越深,布局越复杂,计算、测量等越耗时,就有可能造成丢帧现象,造成卡顿的概率就越高;
  • 相对RelativeLayout而言,优选选择LinearLayout;
  • 可复用的组件抽取出来,使用include标签导入;
  • 不常用的标签用ViewStub;
  • 使用merge标签减少布局的嵌套层次;
  • 在LinearLayout布局中layout_weight属性很耗性能;
  • ListView中的item_view尽量简单,在GetView方法中尽量减少逻辑运算;

3、解放UI线程

顾名思义,我们把一些耗时操作放到子线程中去完成,我们也可以通过打开严苛模式来预防。同时,自定义View的OnDraw方法中尽量减少new对象操作,缓存策略,预加载机制也是我们可以考虑的。

三、分析工具以及使用

本人基本仅借助于AndroidStudio自带的工具和系统设置自带调试工具进行分析以及优化工作;

  • 静态检查:AndroidStudio执行Lint检查操作后,在检查结果Android>Lint>Performance中有许多与性能相关的选项,谨慎修改优化。

  • Monitors:可以直观个查看内存、CPU、GPU、网络情况;

  • [Allocation Tracking]:(Android性能专项测试之Allocation Tracker(Android Studio)):这个可以追踪某一过程中内存分配的情况,在查看内存抖动(涨跌变动大的时候);

  • [Heap Snapshot]:(使用 Android Studio 检测内存泄漏与解决内存泄漏问题):内存泄漏检测分析,我们可以通过手动GC然后获取内存快照来分析,找出造成泄漏的原因;配合Memory Usage来验证自己是否修改正确。
  • Method Tracing: 这个可以分析方法执行耗时、调用次数,点击CPU旁边的那个闹钟样子的按钮开始或者结束记录,可以分析某些耗时方法,来优化流畅度的问题。
  • 系统设置:OverDraw 查找过度绘制问题;
  • 系统设置:GPU呈现模式分析查看UI渲染、分析流畅度问题;

四、个人体会

在性能优化方面,一直研究不深。至今为止,也只是在很浅的层面上借助于工具来进行优化,也顺便了解了部分底层的一些知识。作为一名APP应用开发者,我们的目标不仅只是实现某一个功能,还要关注这个应用是否好用。在内存消耗、体验是否流畅、apk的体积大小等各方面,都直接影响了app的最终质量。其实说到底,性能体验不好,最终的原因还是程序的设计不合理,代码写的不规范,布局不合理等等原因造成的。所以,进行性能优化的工作,不仅提高了应用app的性能,同时也是帮助自己写出更优秀的代码。我们可能不能够想测试工程师那样去深入研究,但是主动防御相比起反攻而言,同样有很大的价值。

五、参考文献

编辑于 2016-11-19