java.lang.OutOfMemoryError: bitmap size exceeds VM budget解决方法

在开发图片浏览器等软件是,很多时候要显示图片的缩略图,而一般情况下,我们要将图片按照固定大小取缩略图,一般取缩略图的方法是使用BitmapFactory的decodeFile方法,然后通过传递进去BitmapFactory.Option类型的参数进行取缩略图,在Option中,属性值inSampleSize表示缩略图大小为原始图片大小的几分之一,即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。

  然而,如果我们想取固定大小的缩略图就比较困难了,比如,我们想将不同大小的图片去出来的缩略图高度都为200px,而且要保证图片不失真,那怎么办?我们总不能将原始图片加载到内存中再进行缩放处理吧,要知道在移动开发中,内存是相当宝贵的,而且一张100K的图片,加载完所占用的内存何止100K?

  经过研究,发现,Options中有个属性inJustDecodeBounds,研究了一下,终于明白是什么意思了,SDK中的E文是这么说的

  If set to true, the decoder willreturn null (no bitmap), but the out... fields will still be set,allowing the caller to query the bitmap without having to allocatethe memory for its pixels.

  意思就是说如果该值设为true那么将不返回实际的bitmap不给其分配内存空间而里面只包括一些解码边界信息即图片大小信息,那么相应的方法也就出来了,通过设置inJustDecodeBounds为true,获取到outHeight(图片原始高度)和outWidth(图片的原始宽度),然后计算一个inSampleSize(缩放值),然后就可以取图片了,这里要注意的是,inSampleSize可能小于0,必须做判断。
具体代码如下:
FrameLayoutfr=(FrameLayout)findViewById(R.id.FrameLayout01);
      BitmapFactory.Options options = newBitmapFactory.Options();
      options.inJustDecodeBounds = true;
       //获取这个图片的宽和高
       Bitmapbitmap = BitmapFactory.decodeFile("/sdcard/test.jpg", options);//此时返回bm为空
      options.inJustDecodeBounds = false;
        //计算缩放比
       int be =(int)(options.outHeight / (float)200);
       if (be<= 0)
          be =1;
      options.inSampleSize = be;
       //重新读入图片,注意这次要把options.inJustDecodeBounds 设为false哦
      bitmap=BitmapFactory.decodeFile("/sdcard/test.jpg",options);
       int w =bitmap.getWidth();
       int h =bitmap.getHeight();
      System.out.println(w+"  "+h);
       ImageViewiv=new ImageView(this);
      iv.setImageBitmap(bitmap);
这样我们就可以读取较大的图片而不会内存溢出了。如果你想把压缩后的图片保存在Sdcard上的话就很简单了:
File file=newFile("/sdcard/feng.png");
       try{
         FileOutputStream out=new FileOutputStream(file);
         if(bitmap.compress(Bitmap.CompressFormat.PNG, 100,out)){
            out.flush();
            out.close();
         }
       } catch(FileNotFoundException e) {
          // TODOAuto-generated catch block
         e.printStackTrace();
       } catch(IOException e) {
          // TODOAuto-generated catch block
         e.printStackTrace();
      }


ok,这样就把图片保存在/sdcard/feng.png这个文件里面了,呵呵。
***********************************************************************************

BitmapFactory.decodeFile(imageFile);

用BitmapFactory解码一张图片时,有时会遇到该错误。这往往是由于图片过大造成的。要想正常使用,则需要分配更少的内存空间来存储。

BitmapFactory.Options.inSampleSize

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

opts.inSampleSize = 4;

Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);

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

opts.inSampleSize = 4;

Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);


设置恰当的inSampleSize可以使BitmapFactory分配更少的空间以消除该错误。inSampleSize的具体含义请参考SDK文档。例如:设置恰当的inSampleSize是解决该问题的关键之一。BitmapFactory.Options提供了另一个成员inJustDecodeBounds。

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

opts.inJustDecodeBounds = true;

Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);

设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。

查看Android源码,Android提供了一种动态计算的方法。

public static int computeSampleSize(BitmapFactory.Options options,

        int minSideLength, int maxNumOfPixels) {

    int initialSize = computeInitialSampleSize(options, minSideLength,

            maxNumOfPixels);



    int roundedSize;

    if (initialSize <= 8) {

        roundedSize = 1;

        while (roundedSize < initialSize) {

            roundedSize <<= 1;

        }

    } else {

        roundedSize = (initialSize + 7) / 8 * 8;

    }



    return roundedSize;

}



private static int computeInitialSampleSize(BitmapFactory.Options options,

        int minSideLength, int maxNumOfPixels) {

    double w = options.outWidth;

    double h = options.outHeight;



    int lowerBound = (maxNumOfPixels == -1) ? 1 :

            (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));

    int upperBound = (minSideLength == -1) ? 128 :

            (int) Math.min(Math.floor(w / minSideLength),

            Math.floor(h / minSideLength));



    if (upperBound < lowerBound) {

        // return the larger one when there is no overlapping zone.

        return lowerBound;

    }



    if ((maxNumOfPixels == -1) &&

            (minSideLength == -1)) {

        return 1;

    } else if (minSideLength == -1) {

        return lowerBound;

    } else {

        return upperBound;

    }

} 

使用该算法,就可动态计算出图片的inSampleSize。

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

opts.inJustDecodeBounds = true;

BitmapFactory.decodeFile(imageFile, opts);

   

opts.inSampleSize = computeSampleSize(opts, -1, 128*128);  

opts.inJustDecodeBounds = false;

try {

 Bitmap bmp = BitmapFactory.decodeFile(imageFile, opts);

 imageView.setImageBitmap(bmp);

    } catch (OutOfMemoryError err) {

    }

另外,可以通过Bitmap.recycle()方法来释放位图所占的空间,当然前提是位图没有被使用。

转载自:http://blog.sina.com.cn/s/blog_672f41790100t35n.html

你可能感兴趣的:(android,算法,移动开发,File,query)