Android性能优化总结

本文大体分为四部分
  1. 内存优化
  2. 布局优化
  3. 编码优化
  4. 网络优化

内存优化

主要参考胡凯文章

首先说一下内存泄漏和OOM:
  1. 内存泄漏,因为不恰当的引用导致本该被释放的资源无法得到释放。
  2. OOM,新分配的内存大小加上已经占用的内存大小,超出了限制的内存大小。

内存泄漏更多是因为我们的代码写的有问题,OOM更多是因为我们对我们应用内存的占用没有很好的把控。内存泄漏是导致OOM的一大元凶。

内存优化分为5点来说:
  1. 减少对象内存占用
  2. 内存对象的复用
  3. 避免内存泄漏
  4. 合理的内存使用策略
  5. 内存优化辅助工具

一、减少对象内存占用

  1. 使用更加轻量的数据结构。比如:ArrayMap/SparseArray是替代HashMap的好帮手。关于ArrayMap和SparseArray的使用:
  • 适合对象个数的数量级最好在千以内,因为他们的插入和删除的效率不够高。
  • 查找和插入使用的是二分查找。
  • key类型是int时,请使用SparseArray因为它避免了自动装箱。
  1. 避免使用Enum。官方说法是,相对于静态常量,枚举会消耗两倍以上的内存。并且在运行时还会产生额外的内存占用。在一个官方实例里,枚举占用的内存是静态常量的13倍,运行时内存占用是6倍。
  2. 减少Bitmap对象的内存占用。对于创建出来的Bitmap对象通常有两个操作可以优化其内存占用。
  • inSampleSize:在载入内存之前,计算一个合适的缩放比例。
  • decode format:解码格式,ARGB_8888(每个像素4个字节,最高精度,有透明通道)、RGB_565(每个像素两个字节,无透明度)、ARGB_4444(deprecated in Api13)、Alpah_8,不同的解码格式差别很大。根据情况选择合适的会比较好。
  1. 使用更小的图片。拿到美工给的图,要留意大图直接被XML引用有时会出现InflationException,该异常的根本原因就是OOM。

二、内存对象的复用

  1. 复用系统自带的资源。系统自带了很多颜色、动画、样式、布局、图片。使用这些可以减少内存开销,但需注意版本差异性。
  2. ListView/GridView/RecyclerView内重复子View的复用。
  3. Bitmap对象复用。使用inBitmap来复用已经存在的内存区域。3.0之后出现,重用的bitmap大小和解码格式需要一致。4.4以后优化大小限制,只要小于或等于原bitmap的大小即可。可以维护一个有多种典型bitmap的对象池,使得后续bitmap创建都可以找到合适的复用模板。
  4. 在频繁调用的方法外创建对象。如onDraw()方法会频繁调用,在里面做创建对象的操作会迅速增加内存使用,很容易引起频繁GC甚至是内存抖动。
  5. StringBuilder/StringBuffer。使用StringBuilder/StringBuffer来替代频繁的字符串拼接操作。

三、避免内存泄漏

  1. Activity的泄漏。
  • 内部类引用导致。典型的如Handler。考虑尽量使用静态内部类,同时使用弱引用机制避免互相引用出现的泄漏。
  • Activity Context被传递到其他实例中,可能导致自身被引用发生泄漏。尽量使用Application Context。除了和UI相关的,如显示弹窗、启动Activity、填充布局。参考Android Context。
  1. 临时Bitmap对象的回收。临时创建一个相对比较大的bitmap对象,在经过变换获得新的bitmap对象之后,应尽快回收之前的bitmap。注意createBitmap()方法可能返回source bitmap,所以需要检查返回值是否和source bitmap相等。不等才可以对source bitmap执行recycle方法。
  2. 监听器的注销。
  3. Cursor对象的及时关闭。
  4. 缓存容器的对象泄漏。如4.0之前,把drawable添加到缓存容器,因为drawable和view的强引用很容易导致activity发生泄漏。
  5. WebView的泄露。Android的WebView存在很大的兼容性问题,WebView因为不同系统版本不同厂商都粗乃很大的差异,甚至标准的WebView存在内存泄漏的问题(09年发现,13年修复)。根治方法:为WebView使用新进程,通过AIDL进行通信,WebView所在进程根据业务需要在合适时机进行销毁。
  6. 慎用static对象,static的生命周期和应用的进程保持一致,使用不当很可能导致内存泄漏。
  7. 留意单例对象中不合理的引用。单例对象的生命周期和应用保持一致。

四、合理的内存使用策略

  1. 使用IntentService代替Service。
  2. 谨慎使用large heap。在清单文件的节点设置largeHeap=true可以为应用生命一个更大的heap控件。会影响用户体验,并使GC运行时间更长,任务切换耗能增加。并且,在一些严格限制的机器上,largeHeap和通常的heap size大小一样。你始终应该通过getMemoryClass()来检查实际获取到的heap大小。
  3. 合适的缓存大小。结合可用内存大小等因素设置。
  4. onLowMemory()和onTrimMemory()。后者从4.0开始提供,提供了更为详细的系统内存占用级别。可以通过监测系统内存占用适当的释放自身的一些内存占用。
  5. 选择合适的文件夹存放资源文件。图片会被拉伸以适应不同的设备。对于不希望被拉伸的图片,放在assets或nodpi目录下。
  6. 对大内存分配做Try...Catch...操作。比如给解析大图时,使用try catch,catch到OOM后将采样比例增加一倍再次尝试解析。
  7. 慎用抽象编程。抽象需要同等量的代码用于可执行,这些代码会被mapping到内存中。
  8. 使用nano protobufs序列化数据。Google设计,类似XML,比XML更加轻量快速简单。
  9. 慎用依赖注入。通过扫描你的代码执行许多初始化操作,会导致你的代码需要大量的内存空间来mapping代码,而且mapped pages会长时间保留在内存中。
  10. 慎用多进程。可以扩大应用的内存占用范围,但使用不当会导致显著增加内存。
  11. 使用ProGuard剔除不需要的代码。
  12. 慎用第三方库。很多功能会用不上。

五、内存优化辅助工具

  1. facebook开源的LeakCanary,可用来监测内存泄漏
  2. Android Monitor,可以查看内存占用,可以查看指向,可以手动触发GC。分析内存泄漏的流程是
  3. 手动触发GC
  4. 查看JavaHeap
  5. 点击Analyzer Task即可进行内存泄漏的分析。

布局优化

分四个方面

  1. 选择合适的根节点
  2. 重用布局文件
  3. 仅在需要时加载布局
  4. 避免过度绘制

一、选择合适的根节点

Android在创建Activity时默认生成的布局为RelativeLayout,而新建布局时默认的根节点为LinearLayout。这是因为

  1. 在复杂的布局中使用RelativeLayout可以降低布局嵌套,使布局比较扁平。也更加灵活。
  2. 对于简单的布局,LinearLayout在不用处理weight属性的情况下,性能上是优于至少需要计算两次的RelativeLayout的。

二、重用布局文件

  1. 标签的使用,要注意如果需要使用layout属性,必需先设置layout:width和layout:height
  2. 标签的使用可减少不必要的视图嵌套 :
  3. 添加的子视图不需要针对父视图的属性,只是要添加到父视图上显示。根节点可为
  4. 比如在LinearLayout里include另外一个方向相同的LinearLayout,这个被include的视图的根节点就可以改为merge.

三、仅在需要时加载布局


使用时调用inflate即可,也可以调用setVisibility(View.VISIBILITY)。
注意:不支持标签的布局。

四、避免过度绘制

比如给根布局设置了图片背景,但是用户只能看到子View,根本就没有看到最下面的背景。但是背景仍要被绘制。这就是过度绘制。
可以通过设置-开发者选项-显示GPU过度绘制来观察过度绘制。颜色越深的区域过度绘制越严重,蓝色最好,红色最差。

  1. 去除不必要的背景设置。
  2. 自定义view时,通过Canvas的clipRect()来绘制部分需要重绘的区域。

五、小知识点

  • android:drawableXXX属性。TextView控件可直接显示图片和文字。
  • setCompoundDrawable(),代码中通过该方法实现第一个效果。
  • android:divider,使用自带的分割线。
  • space控件,可用于添加空白间隔,该控件不进行绘制。
  • android:lineSpacingExtra="",android:text="aaa\nbbb\nccc"多行文字可使用TextView的行间距实现。
  • Spannable,使用Spannable来为TextView设置强大的样式。

编码优化

几个点

  1. 静态方法。将一项通用的功能写成静态方法,调用速度会提升15%-20%,同时不需要创建对象来调用该方法,也不用担心改变对象的状态(静态方法无法访问非静态字段)。
  2. 避免创建不必要的对象
  3. 静态最终常量。对基本数据类型以及String常量使用static final修饰,会在dex文件的初始化器中进行初始化,会更快。
  4. 多使用增强型for循环,但ArrayList使用传统循环方式。
  5. 使用系统封装好的API。系统的API很多功能是通过底层汇编模式执行的,效率会比较高。如数组拷贝的功能,使用System.arrayCopy()会比使用循环一一赋值效率高9倍以上。
  6. 避免在内部使用getter/setter。内部使用时,字段查找比方法调用效率高。

网络优化

工具:Android Studio有Network Monitor

主要有:

  1. 接口设置多样化,便于App可以以较少的请求来完成业务需求。
  2. 使用Gzip压缩request和response,减少数据传输大小
  3. 可以使用Protocol Buffer来代替json、XML等
  4. 合适的图片。获取图片时告知服务器宽高质量等来获取合适的图片资源。
  5. 设置网络缓存,来取消不必要的网络请求。
  6. 在网络良好的时候,对一些很有可能会进行操作的数据进行提前获取。
  7. 弱网优化,Android Emulator可以设置网络速度和延迟来做弱网测试。可采取的措施如:不自动加载图片、先反馈后提交(如用户点赞,先给提交成功的反馈,记录下来之后提交)

你可能感兴趣的:(Android性能优化总结)