`
liu86th
  • 浏览: 113345 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

[ 转]内存管理 --- 避免出现 bitmap内存限制 OUT OF MEMORY 的一种方法

 
阅读更多

 

在编写Android程序的时候,我们总是难免会碰到OOM(OUT OF MEMORY)的错误,那

么这个错误究竟是怎么来的呢,可以先看一下这篇文章 ANDROID BITMAP 内存限制

OOM,OUT OF MEMORY。 

 

这里,我使用 Gallery 来举例,在模拟器中,不会出现 OOM 错误,但是,一旦把程序运

行到真机里,图片文件一多,必然会出现OOM,我们通过做一些额外的处理来避免。 

 

1.创建一个图片缓存对象HashMap dataCache,integer对应Adapter中的位置position,我

们只用缓存处在显示中的图片,对于之外的位置,如果  dataCache 中有对应的图片,我

们需要进行回收内存。在这个例子中,Adapter 对象的 getView 方法首先判断该位置是否

有缓存的  bitmap,如果没有,则解码图片(bitmapDecoder.getPhotoItem,BitmapDecoder

类见后面)并返回 bitmap  对象,设置 dataCache  在该位置上的 bitmap 缓存以便之后使

用;若是该位置存在缓存,则直接取出来使用,避免了再一次调用底层的解码图像需要

的内存开销。有时为了提高  Gallery的更新速度,我们还可以预存储一些位置上的bitmap,

比如存储显示区域位置外向上3个向下 3个位置的bitmap,这样上或下滚动  Gallery时可

以加快getView的获取。

 

 

public View getView(int position, View convertView, ViewGroup parent) 
{ 
       
                             
       
                             if(convertView==null){ 
       
                                     LayoutInflater inflater  = 

LayoutInflater.from(context); 
       
                                     convertView = 
inflater.inflate(R.layout.photo_item, null); 
       
 
       
                         holder = new ViewHolder(); 
       
                         holder.photo = (ImageView) 
convertView.findViewById(R.id.photo_item_image); 
       
                         holder.photoTitle = (TextView) 
convertView.findViewById(R.id.photo_item_title); 
       
                         holder.photoDate = (TextView) 
convertView.findViewById(R.id.photo_item_date); 
       
                         convertView.setTag(holder); 
       
                             }else { 
       
                             holder = (ViewHolder) 
convertView.getTag(); 
       
                         } 
       
                             cursor.moveToPosition(position); 
       
                             
       
                             Bitmap current = 
dateCache.get(position); 
       
                             if(current != null){//如果缓存中已解码
该图片,则直接返回缓存中的图片 
       
                                    
holder.photo.setImageBitmap(current); 
       
                             }else { 
       
                                     current = 
bitmapDecoder.getPhotoItem(cursor.getString(1), 2) ;holder.photo.setImageBitmap(current); 
       
                                     dateCache.put(position, 
current); 
       
                             } 
       
                             
holder.photoTitle.setText(cursor.getString(2)); 
       
                            
holder.photoDate.setText(cursor.getString(4)); 
       
                             return convertView; 
       
                     } 
       
                     
       
             } 

 

BitmapDecoder.class 

 

 

package com.wuyi.bestjoy; 
       
 
       
     import java.io.FileNotFoundException; 
       
     import java.io.FileOutputStream; 
       
 
       
     import android.content.Context; 
       
     import android.graphics.Bitmap; 
       
     import android.graphics.BitmapFactory; 
       
     import android.graphics.Matrix; 
       
 
       
     public class BitmapDecoder { 
       
             private static final String TAG = "BitmapDecoder"; 
  private Context context; 
       
             public BitmapDecoder(Context context) { 
       
                     this.context = context; 
       
             } 
       
             
       
             public Bitmap getPhotoItem(String filepath,int size) { 
       
                   BitmapFactory.Options options = new 
BitmapFactory.Options(); 
       
                     options.inSampleSize=size; 
       
                     Bitmap bitmap = 
BitmapFactory.decodeFile(filepath,options); 
       
                     bitmap=Bitmap.createScaledBitmap(bitmap, 180, 
251, true);//预先缩放,避免实时缩放,可以提高更新率 
       
                   return bitmap; 
       
                    
       
             } 
       
     }

 

 

2.由于Gallery控件的特点,总有一个item处于当前选择状态,我们利用此时进行

dataCache中额外不用的bitmap的清理,来释放内存。 

 

 

 @Override 
       
             public void onItemSelected(AdapterView<?> parent, View 
view, int position,long id) { 
       
                     
       
                     releaseBitmap(); 
       
                     Log.v(TAG, "select id:"+ id); 
      
             } 
       
 
       
     private void releaseBitmap(){ 
       
         //在这,我们分别预存储了第一个和最后一个可见位置之外的3个
位置的bitmap 
       
         //即dataCache中始终只缓存了(M=6+Gallery当前可见view的个
数)M个bitmap 
       
                     int start = mGallery.getFirstVisiblePosition()-3; 
       
                     int end = mGallery.getLastVisiblePosition()+3; 
       
                     Log.v(TAG, "start:"+ start); 
       
                     Log.v(TAG, "end:"+ end); 
       
                     //释放position<start之外的bitmap资源 
       
                     Bitmap delBitmap; 
       
                     for(int del=0;del<start;del++){ 
       
                             delBitmap = dateCache.get(del); 
       
                             if(delBitmap != null){ 
       
                                      //如果非空则表示有缓存的
bitmap,需要清理 
       
                                     Log.v(TAG, "release position:"+ 
del); 
       
                                     //从缓存中移除该del->bitmap的
映射 
       
                                     dateCache.remove(del); 
       
                                      delBitmap.recycle(); 
       
                             } 
                     } 
       
 
       
                     freeBitmapFromIndex(end); 
       
                     
       
             } 
       
             
       
             /** 
       
              * 从某一位置开始释放bitmap资源 
       
              * @param index 
       
              */ 
       
             private void freeBitmapFromIndex(int end) { 
       
                     //释放之外的bitmap资源 
       
                     Bitmap delBitmap; 
       
                      for(int del =end+1;del<dateCache.size();del++){ 
       
                             delBitmap = dateCache.get(del); 
       
                             if(delBitmap != null){ 
       
                                     dateCache.remove(del); 
       
                                     delBitmap.recycle(); 
       
                                     Log.v(TAG, "release position:"+ 
del); 
       
                             } 
       
                             
       
                     } 
       
             }
 

经过这些额外的操作,有效的避免了OOM的问题。 

 

内存溢出的解决办法 

 

 

在模拟器上给 gallery 放入图片的时候,出现 java.lang.OutOfMemoryError: bitmap size 

exceeds VM budget 异常,图像大小超过了RAM内存。   

 

      模拟器RAM比较小,只有8M内存,当我放入的大量的图片(每个100 多 K左右),

就出现上面的原因。由于每张图片先前是压缩的情况。放入到 Bitmap的时候,大小会变

大,导致超出RAM内存,具体解决办法如下:   

 

//解决加载图片 内存溢出的问题   

                     //Options 只保存图片尺寸大小,不保存图片到内存   

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

                 //缩放的比例,缩放是很难按准备的比例进行缩放的,其值表明缩放的倍数,

SDK中建议其值是 2的指数值,值越大会导致图片不清晰   

                 opts.inSampleSize = 4;   

                 Bitmap bmp = null;   

                 bmp = BitmapFactory.decodeResource(getResources(), 

mImageIds[position],opts);                                

 

                 ...                 

 

                //回收   

                 bmp.recycle();   

 

通过上面的方式解决了,但是这并不是最完美的解决方式。

 

优化 Dalvik虚拟机的堆内存分配

 

 

对于 Android 平台来说,其托管层使用的 Dalvik Java VM 从目前的表现来看还有很多地

方可以优化处理,比如我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉 GC

处理,使用  dalvik.system.VMRuntime 类提供的 setTargetHeapUtilization 方法可以增强

程序堆内存的处理效率。当然具体 原理我们可以参考开源工程,这里我们仅说下使用方

法:    private final static float TARGET_HEAP_UTILIZATION = 0.75f; 在程序onCreate时

就可以调用  VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 

即可。   

 

Android 堆内存也可自己定义大小   

 

 

  对于一些 Android 项目,影响性能瓶颈的主要是 Android 自己内存管理机制问题,目

前手机厂商对 RAM 都比较吝啬,对于软件的流畅性来说 RAM 对 性能的影响十分敏感,

除了 优化 Dalvik 虚拟机的堆内存分配外,我们还可以强制定义自己软件的对内存大小,

 

我们使用Dalvik提供的  dalvik.system.VMRuntime类来设置最小堆内存为例:   

 

private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;   

 

VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //设置最小 heap 内

存为6MB大小。当然对于内存吃紧来说还可以通过手动干涉GC去处理   

 

 

bitmap 设置图片尺寸,避免 内存溢出  OutOfMemoryError的优化方法   

★android 中用bitmap 时很容易内存溢出,报如下错误: Java.lang.OutOfMemoryError : 

bitmap size exceeds VM budget   

 

● 主要是加上这段:   

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

                 options.inSampleSize = 2;   

 

● eg1:(通过Uri取图片)   

private ImageView preview;   

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

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

的四分之一   

                     Bitmap bitmap = BitmapFactory.decodeStream(cr   

                             .openInputStream(uri), null, options);   

                     preview.setImageBitmap(bitmap);   

以上代码可以优化内存溢出,但它只是改变图片大小,并不能彻底解决内存溢出。   

● eg2:(通过路径去图片)   

private ImageView preview;   

private String fileName= "/sdcard/DCIM/Camera/2010-05-14 16.01.44.jpg";   

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

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

四分之一   

                         Bitmap b = BitmapFactory.decodeFile(fileName, options);   

                         preview.setImageBitmap(b);   

                         filePath.setText(fileName);   

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics