-1

In below code, I am getting exception "Out of memory on a byte allocation" for a large size images in function "getScaledBitmap" when processing decodeFile second time in code. This all below function is being called 4 times, as I am processing 4 different images on a screen.

Please guide on this.

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;    
     }
RaRa
  • 2,024
  • 1
  • 18
  • 29

2 Answers2

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;}



    //     }
       }  

method to optimize imagefile

 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;

            }
Karthika PB
  • 1,373
  • 9
  • 16
  • o.inPurgeable=true; o.inInputShareable=true; this method is deprecated in lolipop – RaRa Apr 09 '15 at 06:10
  • @Ramani oh I applied this in an old app and it's not supporting lolipop.pLEASE TRY THE SAME COMMENTING THE DEPRECATED LINES AND CHECK IF IT WORKS. – Karthika PB Apr 09 '15 at 06:41
0

On many phones, an application has enough RAM for exactly 1 (one) image, loading the 2nd one always results in out-of-memory exception. Even if you can load 2 photos in RAM on your phone, another phone will have a better camera and it will fail. If you want to display large images on the screen, you have to scale them. And if you want to, say, merge two large images, well, you have a problem. I'd suggest doing this in a separate process (either a command line tool or a service). Note that the application can ask for large heap in the manifest: android:largeHeap=["true" | "false"] (link). But most likely you can just avoid loading two images in RAM.

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127