Android内存问题之bimap

最近的项目中,展现的图片比较多,都是通过手机上传的图片,现在手机相机的分辨率都是一个赛一个的高。因此在展示图片的时候就出现了图片出现OOM,因此把之前的问题整理一下。

由于Android系统的手机在系统底层上指定了堆内存的上限值,大部分手机的缺省值是16MB,部分高配置的机型也会设置位24MB,因此在申请内存空间的时候,要保证能够成功的申请到内存空间,应该保证当前已经分配的内存,加上需要分配的内存值的总大小不要超过当前堆的最大内存值。而且内存管理上将外部内存完全当成了当前堆的一部分,即Bitmap对象通过栈上的饮用来指向堆上的Bitmap对象,而堆上的Bitmap对象又对应了一个实用外部存贮的native对象,实际上实用的字节数组byte[]来存储的位图信息,因此解码后的Bitmap的总大小就不能超过8M了。

解决此类问题的一般办法就是,实用完Bitmap之后,调用Bitmap对象的recycle()方法释放所占用的内存。

在解决的时候,参考了往上的多种方法,有如下优化方法:

1. 设置系统的最小堆的大小: int newSize = 4*1024*1024;// set the minisize of the heap size: 4MB.

VMRuntime.getRuntime().setMinimumHeapSize(newSize);

VMRuntime.getRuntime().setTargetHeapUtilization(0.75);// set the memory useage is 75%.

PS:堆(heap)是VM中占用内存最多的部分,通常都是动态分配的。堆的大小不是一成不变的,当对内存实际的利用率偏离设定的值的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向这个百分比靠拢,例如厨师设置的HEAP是4M当4M空间被使用超过75%的时候,重新分配堆为8M大小,8M被占用超过75%,分配堆为16M大小。当16M的利用率不足30%的时候,缩减它的大小为8M。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小堆效率有不良的影响。

2. 对图片大小进行控制

BitmapFactory.Options options = new BitmapFactory,Options();

options.inSampleSize = 2;// 图片的 宽高都为原来的二分之一,即图片为原来的四分之一。

Bitmap bimap = BitmapFactory.decodeFile("",options);//在需要保证图片质量的时候不可取。

3. 存放到临时存储空间

BitmapFactory.Options options = new BitmapFactory,Options();

options.inTempStorage = new byte[1024*1024*5]; // 5M的临时存储空间

Bitmap bimap = BitmapFactory.decodeFile("",options);//

从创建Bitmap的C++底层代码BitmapFactory.cpp中的处理逻辑来看,如果option不为null的话,那么会优先处理option中设置的各个参数,假设当前你设置option的inTempStorage为1024*1024*4(4M)大小的话,而且每次解码图像时均使用该option对象作为参数,那么你的程序极有可能会提前失败,经过测试,如果使用一张大小为1.03M的图片来进行解码,如果不使用option参数来解码,可以正常解码四次,也就是分配了四次内存,而如果使用option的话,就会出现内存溢出错误,只能正常解码两次。Options类有一个预处理参数,当你传入options时,并且指定临时使用内存大小的话,Android将默认先申请你所指定的内存大小,如果申请失败,就会先抛出内存溢出错误。而如果不指定内存大小,系统将会自动计算,如果当前还剩3M空间大小,而解码只需要2M大小,那么在缺省情况下将能解码成功,而在设置inTempStorage大小为4M的情况下就将出现内存溢出错误。所以,通过设置Options的inTempStorage大小也不能从根本上解决大图像解码的内存溢出问题。


4. 对图片采用阮饮用,及时的进行recycle()

  1. SoftReference<Bitmap> bitmap;  
  2.              bitmap = new SoftReference<Bitmap>(pBitmap);  
  3.  
  4.  if(bitmap != null){  
  5.          if(bitmap.get() != null && !bitmap.get().isRecycled()){  
  6.              bitmap.get().recycle();  
  7.              bitmap = null;  
  8.          }  
  9.      } 

5. 对Adapter中的convertView进行重用。


6. 实用lazy loading data

  1. public View getView(int position, View convertView, ViewGroup parent) {  
  2.  if (convertView == null) {  
  3.             v = mInflater.inflate(resource, parent, false);  
  4.             final int[] to = mTo;  
  5.             final int count = to.length;  
  6.             final View[] holder = new View[count];  
  7.  
  8.             for (int i = 0; i < count; i++) {  
  9.                 holder[i] = v.findViewById(to[i]);  
  10.             }  
  11.             v.setTag(holder);  
  12.         } else {  
  13.     }   
  14. }  

7. 在进行横竖屏切换N次后,也会出现OOM

查看页面布局当中有没有大的图片,例如:背景图片,去除XML中的设置,改为程序中设置背景图片。在onCreate()中添加:

  1. Drawable bg = getResources().getDrawable(R.drawable.bg);  
  2. XXX.setBackgroundDrawable(rlAdDetailone_bg);  
在Activity destory时注意,bg.setCallback(null); 防止Activity得不到及时的释放。

或者,直接把xml配置文件加载成view 再放到一个容器里,然后直接调用 this.setContentView(View view);避免xml的重复加载。

还有,在页面切换时尽可能少地重复使用一些代码。比如:重复调用数据库,反复使用某些对象等等.....**********


其中由于Android的版本不同,bitmap data在不同版本存放的位置是不同的,3.0以前的版本分配在native heap上,3.0以后是分配在VM heap上。

如何把图片存放到native heap里就行了, BitmapFactory里就几个decode方法,其中BitmapFactory.decodeStream就可以解决。

BitmapFactory.Options options = new BitmapFactory.Options();

options.inPreferredConfig = Config.ARGB_8888;

options.inPurgeable = true;// 允许可清除

options.inInputShareable = true;// 以上options的两个属性必须联合使用才会有效果

String sname = String.format( “xxx.png”, sTowerStyle, j, sDirction, i);

InputStream is = am.open(sname);

arrBmp[ iBmpIndex] = BitmapFactory.decodeStream(is, null, options);

这个可以解决加载大量小图片的问题。



你可能感兴趣的:(Android内存问题之bimap)