OOM 的解决方法

###OOM产生的原因

  • Android原生系统规定对一个Application的内存限制为16M。(高分辨率的手机内存限制会高于16M,但是我们在编写程序的时候应当以最低标准为准,这样我们的应用就会更加健壮) 对于这一点我们最好可以按照屏幕的分辨率来加载不同解析度的图片解析度的图片,因为在低分辨率的屏幕上加载高解析度的图片只是白白浪费内存再不会有任何别的好处,因为低分辨率的屏幕显示高解析度的图片和显示低解析度的图片没什么区别。

###OOM的解决方法

####从图片资源本身入手
对于图片选择这个问题,在不到万不得已的情况下,可以用低解析度的图片就用低解析度的图片。图片的解析度低占用的内存就少,出现OOM的可能就小。

####从程序本身入手

  • 在加载图片时,使用BitmapFactory.decodeFromStream()方法,因为该方法会通过直接调用JNI>>nativeDecodeAsset()來完成decode,无需再使用Java层的createBitmap(),这样就节省了Java层的内存空间。
    代码如下:
1
2
3
4
5
6
public Bitmap getLocalBitmap(Context context,int resourceId){
InputStream inputStream = context.getResource()
.openRawResource(resourceId);
return BitmapFactory.decodeStream(inputStream,null
,getBitmapOptions());
}
  • 設定 BitmapFactory.Options inSampleSize,(inSampleSize值越大解析度越小,占用的memory也越小)
1
2
3
4
5
6
7
8
9
/** int scale 为图片的缩放度*/
public BitmapFactory.Options getBitmapOptions(int scale){
BitmapFactory.Options options = new BitmapFactory.Options();
//设置为true,如果系统内存不足,可回收图片内存
options.inPurgeable = true;
options.inInputShareable = true;//和inPurgeable属性一起使用
options.inSampleSize = scale;//解析度,值越大返回的图片尺寸越小(2~4)
return options;
}
  • 在onPause(),onStop(),onDestory()的时候将不用的Bitmap release掉。
1
2
bitmap.recycle();
System.gc();
  • 另外在BitmapFactory.Options里有一个隐藏的public参数inNativeAlloc,将这个参数设置为true,这样会将OOM的可能性更进一步降低。
1
2
BitmapFactory.Options.class.getFiled("inAlloc")
.setBoolean(options,true);
  • 我们也可以通dlvik.system.VMRuntime来修改系统分配给应用的内存大小
1
2
3
private static final int CUS_MAX_VM_HEAP = 32*1024*1024;
VMRuntime.getRuntime()
.setMinmumHeapSize(CUS_MAX_VM_HEAP);
  • 一般情况下不推荐使用createBitmap来创建图片,但是如果必须要用可以进行如下优化:
1
2
3
4
5
6
7
8
//透明度占8位
Bitmap.createBitmap(width,height,Bitmap.Config_ALPHA_8);
//每个Pixel占4个像素
Bitmap.createBitmap(width,height,Bitmap.Config_ARGB_4444);
//每个Pixel占8个像素(常用Config)
Bitmap.createBitmap(width,height,Bitmap.Config_ARGB_8888);
//没有透明度,R占5位,B占6位,G占5位
Bitmap.createBitmap(width.height,Bitmap.Config_RGB_565);
  • 在调用setBackgroundResource(int resourceId)时,系统会为了性能问题,将id对应的图片缓存起来。当我们在onDestory中将background中的Bitmap recycle掉时,如果我们此时back到这个Activity中的时候,系统会报use a recycled bitmap execption,因为系统会调用缓存的Bitmap,但是这个Bitmap已经被我们释放掉了。为了避免这个问题的发生我们可以不使用系统缓存:
1
2
3
4
5
6
InputStream inputStream = context.getResource()
.openRawResource(id);
Bitmap bitmap = BitmapFactory.decodeStreame();
mButton.setBackground(
new BitmapDrawable(getResource(),bitmap));
//代替mButton.setBackgroundResoure(id);

在销毁图片的时候如下:

1
2
3
4
BitmapDrawable bd = (BitmapDrawable)mButton
.getBackground();
bd.getBitmap.recycle();
bd = null;

按照以上方式我们可以根据不同的需求来创建Bitmap来防止OOM的发生。