- 使用LocalBroadcastManager
使用Context的registerReceiver和sendBroadCast注册发送广播是全局范围的,或者说是整个系统范围的,而LocalBroadcastManager限定只在我们的app进程中,不用担心广播发送的数据往外泄露,同时外部应用也无法通过广播调起我们的app,因此更加安全。
- convertView复用
如果使用ListView或者GridView,注意convertView复用以及ViewHolder的使用,可以不用每次都创建新的布局以及进行findViewById这样费时的操作,即使某种itemType类型的itemView数量只有一个。推荐使用RecyclerView,功能更强大,性能更好,并且为我们封装好了View复用工作。
- 合理使用Context
如果需要保存Context信息,尽量使用ApplicationContext,以防内存泄漏。不过,ApplicationContext也不是万能的,ApplicationContext不能用来创建Dialog,不能直接用来启动Activity(需要设置FLAG_ACTIVITY_NEW_TASK标识位),使用ApplicationContext创建的布局使用的是默认的主题样式。如果使用的不是ApplicationContext,切记及时释放Context。
- 尽量避免使用非静态内部类
Android中创建内部类的情况有很多,例如创建ViewHolder,Adapter等等。如果可以,应该尽量使用静态内部类。一方面,静态内部类不持有外部类的引用,可以一定程度上防止内存泄漏的风险;另一方面,静态内部类不能访问外部类非静态方法,编译时不用为内部类生成访问外部类的接口。
- SparseArray替代HashMap
SparseArray底层keys和values分别使用数组存储,添加、删除等操作使用binary search二分查找;使用int基本类型作为key类型,避免autobox带来的性能损失;另外,删除操作并不会真的直接移除key和value,只是做了一个DELETED标记,特定时候再统一gc。类似的还有SparseBooleanArray、SparseIntArray、SparseLongArray。
- 尽量避免使用枚举类型enum
Android对于内存较为敏感,而enum会带来额外的内存开销,在没有必要需求的情况下,例如,定义几个int类型常量type,可以考虑使用final static int代替。官方文档说大概会比静态常量大2倍多。具体计算分析可以参考这篇介绍:Android 中的 Enum 到底占多少内存?该如何用?。
- onPause、onStop释放不必要的资源
在Activity跳转时,onPause和onStop会回调,可以释放一些资源,例如暂停视频音频播放,暂停动画等。但是,同时需要注意的是,不要在onPause中做过多复杂的操作。因此我们知道Activity跳转时,会先回调当前Actiity的onPause方法,如果操作过于复杂,会明显影响Activity跳转速度,带来不好的用户体验。
- onDraw最好不要进行new操作
很容易造成内存频繁分配、回收,造成内存抖动现象,加大Dalvik虚拟机gc压力。
- view过度绘制及优化
避免background重复绘制,耗性能。可适当使用ViewStub标签优化性能,减少不必要view的绘制。自定义组件时,可考虑使用merge标签减少层级。使用ReleativeLayout代替LinearLayout完成复杂布局,减少layout层级,单就ReleativeLayout和LinearLayout来说,ReleativeLayout性能不如LinearLayout,但是如果可以减少层级,是划算的。
- 尽量避免使用依赖注入框架
大部分依赖注入框架,都是基于反射机制实现,编码方便的同时其实降低了App性能。
- gradle开启ProGuard选项
开启ProGuard选项,打包时可以剔除多余的代码,并且对代码进行混淆,保证代码安全的同时,一定程度上也可以减小dex包大小。需要注意,如果代码中使用了反射可能会报错,可以在proguard rule中配置不对某些特定的类进行混淆来解决问题。另外,开启了ProGuard可能对调试也有影响,导致断点走不到或者查看不到变量值。解决办法是只在release环境下才开启proguard选项,debug模式下不开启。
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
- Bitmap使用优化
通常原始的图片大小并不是我们需要的,有时为了节省内存,在使用ImageView控件加载图片前,我们可以设置需要加载到内存中的bitmap大小。例如:
public static Bitmap resizeBitmap(String filename, int requiredWidth, int requiredHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(filename, options);
options.inSampleSize = computeSampleSize(options.outWidth, options.outHeight, requiredWidth, requiredHeight);
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(filename, options);
return bitmap;
}
BitmapFactory.Options的inJustDecodeBounds属性设置为true时,不真正加载bitmap到内存中,但是bitmap的宽高等属性都能获取到。经过比例缩放计算后,我们可以设置BitmapFactory.Options的inSampleSize值,表示bitmap的采样大小,即宽高为原始宽高的几分之一,这时候再加载就可以达到节省内存的效果。注意:inSampleSize的值会自动向下优化为最接近的2的n次方。