自从ART横空出世,5.0的系统让每一个安卓手机都运行如飞,作为开发者心里那根资源紧俏的弦终于可以松缓一下了。然而,系统分配的内存空间却并没有变大,依然是16m,所以清扫行动还是要进行的。下面列举了内存优化的几类方法,大家对内存也要且用且珍惜。
一.用完后要手动关闭对象
例如HTTP、Socket、Uri;File、Stream、ContendProvider、SQLite的Cursor;Bitmap、属性动画,这些在用完后都要手动关闭,否则会内存泄漏。
二.生命周期结束时关闭对象
有一些对象需要在它所在的组件生命周期结束时关闭对象,如下所示。
BroadcastReceiver泄露,手动注册广播时,记住退出的时候要unregisterReceiver()。
Service要及时关闭,比如图片上传,当上传成功后,要stopself()。
Handler泄露,当退出activity时,要注意所在Handler消息队列中的Message是否全部处理完成,可以考虑用removeCallbacksAndMessages(null)手动关闭。
第三方SDK/开源框架泄露,ShareSDK、ButterKnife、JPush等第三方SDK需要按照文档控制生命周期。
EventBus等观察者模式的框架需要手动解除注册。
二.static的使用
少使用内部类:内部类会持有外部类的引用,如果外部类是一个Application或者Activity则该内部类也会长期占用内存。
多使用静态方法:静态方法会让调用的速度提升15%-20%,同时也不用为了调用这个方法而去专门创建对象了。
慎用静态变量。
三.final的使用
对常量使用static final修饰符。static final int intVal = 42;
四.Bitmap
1.先从BitmapFactory.options中获取宽高,根据控件大小缩放图片后再加载到内存。
2.不同分辨率文件夹放不同分辨率图片。一般会选择2到3个文件夹放主流手机分辨率。
3.缓存图片的LruCache和DiskLruCache。
五.View缓存
把已经inflate过的view用成员变量保存起来,判断是否为null,是的话再inflate()加载视图。通常的使用场景如adapter的getView()方法,Fragment的onCreateView()方法。
六.Context的不当使用
只在界面层使用Activity的context,否则最好使用ApplicationContext。
七.尽量避免使用Ioc框架
这些框架为了要搜寻代码中的注解,通常都需要经历较长的初始化过程,并且还可能将一些你用不到的对象也一并加载到内存当中。这些用不到的对象会一直占用着内存空间,可能要过很久之后才会得到释放,相较之下,也许多敲几行看似繁琐的代码才是更好的选择。看butterknife和dagger2就是生成了一些中间代码。
八.使用ProGuard简化代码
除了混淆之外,它还具有压缩和优化代码的功能。ProGuard会对我们的代码进行检索,删除一些无用的代码,并且会对类、字段、方法等进行重命名,重命名之后的类、字段和方法名都会比原来简短很多,这样的话也就对内存的占用变得更少了。
九.Java优化
1.不使用枚举
枚举通常会比使用静态常量要消耗两倍以上的内存,在Android开发当中我们应当尽可能地不使用枚举。
2.使用StringBuffer
如果我们有一个需要拼接的字符串,那么可以优先考虑使用StringBuffer或者StringBuilder来进行拼接,而不是加号连接符,因为使用加号连接符会创建多余的对象,拼接的字符串越长,加号连接符的性能越低。
当一个方法的返回值是String的时候,通常可以去判断一下这个String的作用是什么,如果我们明确地知道调用方会将这个返回的String再进行拼接操作的话,可以考虑返回一个StringBuffer对象来代替,因为这样可以将一个对象的引用进行返回,而返回String的话就是创建了一个短生命周期的临时对象。
3.使用基本类型而不是包装类型
在没有特殊原因的情况下,尽量使用基本数据类来代替封装数据类型,int比Integer要更加高效,其它数据类型也是一样。
基本数据类型要优于对象数据类型,类似地,基本数据类型的数组也要优于对象数据类型的数组。另外,两个平行的数组要比一个封装好的对象数组更加高效,举个例子,Foo[]和Bar[]这样的两个数组,使用起来要比Custom(Foo,Bar)[]这样的一个数组高效得多。
4.使用增for each循环
增强型for循环(也被称为for-each循环)可以用于去遍历实现Iterable接口的集合以及数组,这是jdk 1.5中新增的一种循环模式。当然除了这种新增的循环模式之外,我们仍然还可以使用原有的普通循环模式,只不过它们之间是有效率区别的。唯一需要注意的是这种写法需要JDK 1.5之后才支持。提一个特殊情况,对于ArrayList这种集合,自己手写的循环要比增强型for循环更快,而其他的集合就没有这种情况。因此,对于我们来说,默认情况下可以都使用增强型for循环,而遍历ArrayList时就还是使用传统的循环方式吧。
5.避免在内部调用Getters/Setters方法
在Android上这个技巧就不再是那么的受推崇了,因为字段搜寻要比方法调用效率高得多,我们直接访问某个字段可能要比通过getters方法来去访问这个字段快3到7倍。
十.调优工具
1.Android Monitor—内存泄露
在AndroidStudio最下面的Android Monitor。包括内存、CPU、网络、GPU等。
2.Android Device Monitor—内存泄露
i.在Android Device Monitor中,选择要监听的线程,点击菜单栏“Update Heap”,点击“Cause GC”,会显示当前内存状态。不停点击“Cause GC”,看“data object”中的“Total Size”,有明显变化就代表内存泄漏。
ii.点击“Dump HPROF File”。
iii.分析HPROF文件。
3.LeakCanary—内存泄露
使用LeakCanary框架可以检查是否有内存泄漏,更简洁直观。
网友评论