Android - 选择非常大的图像时,应用程序会因内存不足而崩溃

时间:2015-04-01 14:11:20

标签: android

在下面的代码中,我得到异常"字节分配时内存不足"对于函数中的大尺寸图像" getScaledBitmap"在代码中第二次处理decodeFile时。由于我正在屏幕上处理4个不同的图像,所以下面这个函数被调用了4次。

请指导一下。

private Bitmap processimage(String picturePath){
    Bitmap thumbnail =null;
    thumbnail=getScaledBitmap(picturePath,500,500);
    Matrix matrix = null;
     try {

            ExifInterface exif = new ExifInterface(picturePath);
            int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); 
            int rotationInDegrees = bal.exifToDegrees(rotation);
            matrix = new Matrix();
            if (rotation != 0f) {
                matrix.preRotate(rotationInDegrees);
                thumbnail=Bitmap.createBitmap(thumbnail, 0,0, thumbnail.getWidth(), thumbnail.getHeight(), matrix, true);   
            }


        } catch (IOException e) {
            e.printStackTrace();
        }catch (Exception e) {
         e.printStackTrace();
        }

     return thumbnail;
}

protected Bitmap getScaledBitmap(String picturePath, int width, int height) {
    Bitmap result=null;
    try{
        BitmapFactory.Options sizeOptions = new BitmapFactory.Options();
        sizeOptions.inJustDecodeBounds = true;

        BitmapFactory.decodeFile(picturePath, sizeOptions);
        int inSampleSize = calculateInSampleSize(sizeOptions, width, height);

        sizeOptions.inJustDecodeBounds = false;
        sizeOptions.inSampleSize = inSampleSize;

        result=BitmapFactory.decodeFile(picturePath, sizeOptions);

    }catch(Exception ex){
        ex.printStackTrace();
    }

    return result;
}
protected int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        while ((halfHeight / inSampleSize) > reqHeight
                || (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }

    }
    return inSampleSize;
}

public  int exifToDegrees(int exifOrientation) {        
        if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; } 
        else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {  return 180; } 
        else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {  return 270; }            
        return 0;    
     }

2 个答案:

答案 0 :(得分:0)

to scale image and to catch oom error  I use the following code(but for one image).I am not sure if it will be ok for you..and include try catch for OOM in your code to avoid crash

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

          options.inScaled = false;
          options.inDither = false;
          options.inPreferredConfig = Bitmap.Config.ARGB_8888;

       imgBitmap=decodeFile(file);  
                  }
       catch(OutOfMemoryError e){ 
           System.out.println("out of memory");
           flag=1;

               System.out.println("clearing bitmap????????????");
               if (imgBitmap!=null) { 
               this.setBackgroundResource(0);
               this.clearAnimation();
           imgBitmap.recycle(); 
            imgBitmap = null;}



    //     }
       }  

优化图像文件的方法

 public Bitmap decodeFile(File f) {

                try {
                    // decode image size
                    BitmapFactory.Options o = new BitmapFactory.Options();
                    o.inJustDecodeBounds = true;
                    o.inDither=false;                     //Disable Dithering mode
                    o.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
                    o.inInputShareable=true; 
                    o.inPreferredConfig = Bitmap.Config.ARGB_8888;
                    //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
                    o.inTempStorage=new byte[16*1024]; 

                    BitmapFactory.decodeStream(new FileInputStream(f), null, o); 

                    // Find the correct scale value. It should be the power of 2.
                     int REQUIRED_SIZE = 300;

                    int width_tmp = o.outWidth, height_tmp = o.outHeight; 

                    if(REQUIRED_SIZE  > width_tmp)
                    REQUIRED_SIZE  = width_tmp;
                    int scale = 1;
                    while (true) {
                        if (width_tmp / 2 < REQUIRED_SIZE
                                || height_tmp / 2 < REQUIRED_SIZE) 
                            break; 
                        width_tmp /= 2;
                        height_tmp /= 2;
                        scale *= 2;
                        System.out.println(scale+"______________________________-");  
                    }

                    // decode with inSampleSize
                    BitmapFactory.Options o2 = new BitmapFactory.Options();

                    o2.inDither=false;
                    o2.inScaled = false;

                    o2.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
                    o2.inInputShareable=true; 
                    //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
                    o2.inTempStorage=new byte[24*1024]; 
                    o2.inSampleSize = 2; 
                    o2.outWidth = width_tmp;
                    o2.outHeight = height_tmp;
                    o2.inPreferredConfig = Bitmap.Config.ARGB_8888;
                    try {
                        BitmapFactory.Options.class.getField("inNativeAlloc").setBoolean(o2,true);    

                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        } catch (SecurityException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {  
                            e.printStackTrace();
                        } catch (NoSuchFieldException e) {
                            e.printStackTrace();
                        }


                    o2.inJustDecodeBounds = false;
                    return BitmapFactory.decodeStream(new FileInputStream(f), null,  
                            o2);
                } 

                catch (FileNotFoundException e) {
                    System.out.println("file not found");
                }
                return null;

            }

答案 1 :(得分:0)

在许多手机上,一个应用程序有足够的RAM用于准确的1(一)个图像,加载第二个图像总是会导致内存不足异常。即使您可以在手机的RAM中加载2张照片,另一部手机也会有更好的相机,但它会失败。如果要在屏幕上显示大图像,则必须缩放它们。 如果你想要合并两个大图像,那么,你有问题。我建议在一个单独的进程(命令行工具或服务)中执行此操作。请注意,应用程序可以在清单中请求大堆:android:largeHeap=["true" | "false"]link)。但最有可能的是你可以避免在RAM中加载两个图像。