Suggestions to avoid bitmap Out of Memory error

46,514

Solution 1

just use this function to decode...this is perfect solution for your error..because i also getting same error and i got this solution..

public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
     try {
         //Decode image size
         BitmapFactory.Options o = new BitmapFactory.Options();
         o.inJustDecodeBounds = true;
         BitmapFactory.decodeStream(new FileInputStream(f),null,o);

         //The new size we want to scale to
         final int REQUIRED_WIDTH=WIDTH;
         final int REQUIRED_HIGHT=HIGHT;
         //Find the correct scale value. It should be the power of 2.
         int scale=1;
         while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
             scale*=2;

         //Decode with inSampleSize
         BitmapFactory.Options o2 = new BitmapFactory.Options();
         o2.inSampleSize=scale;
         return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
     } catch (FileNotFoundException e) {}
     return null;
 }

Solution 2

Hi you have to decode the file . for this try with the following method.

  public static Bitmap new_decode(File f) {

        // 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; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the future
        try {
            BitmapFactory.decodeStream(new FileInputStream(f), null, o);
        } catch (FileNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        // Find the correct scale value. It should be the power of 2.
        final int REQUIRED_SIZE = 300;
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 1.5 < REQUIRED_SIZE && height_tmp / 1.5 < REQUIRED_SIZE)
                break;
            width_tmp /= 1.5;
            height_tmp /= 1.5;
            scale *= 1.5;
        }

        // decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        // o2.inSampleSize=scale;
        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; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the future
        // return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        try {

//          return BitmapFactory.decodeStream(new FileInputStream(f), null,
//                  null);
            Bitmap bitmap= BitmapFactory.decodeStream(new FileInputStream(f), null, null);
            System.out.println(" IW " + width_tmp);
            System.out.println("IHH " + height_tmp);           
               int iW = width_tmp;
                int iH = height_tmp;

               return Bitmap.createScaledBitmap(bitmap, iW, iH, true);

        } catch (OutOfMemoryError e) {
            // TODO: handle exception
            e.printStackTrace();
            // clearCache();

            // System.out.println("bitmap creating success");
            System.gc();
            return null;
            // System.runFinalization();
            // Runtime.getRuntime().gc();
            // System.gc();
            // decodeFile(f);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }

    }

Solution 3

By Reducing/Scale size of the Image you can get rid out of the Out of Memory Exception, Try this

  BitmapFactory.Options options = new BitmapFactory.Options();
  options.inSampleSize = 6; 
  Bitmap receipt = BitmapFactory.decodeFile(photo.toString(),options);  //From File You can customise on your needs. 
Share:
46,514
Y2theZ
Author by

Y2theZ

Updated on October 20, 2021

Comments

  • Y2theZ
    Y2theZ over 2 years

    I am working on an android application. The application has a view containing lots of image. I had an error, I will try to give as much information as possible hoping someone can give me some suggestions.

    The application was working great on all the local testings. However, I received lots of crashes from users: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    This is the stack trace

    0       java.lang.OutOfMemoryError: bitmap size exceeds VM budget
    1   at  android.graphics.Bitmap.nativeCreate(Native Method)
    2   at  android.graphics.Bitmap.createBitmap(Bitmap.java:507)
    3   at  android.graphics.Bitmap.createBitmap(Bitmap.java:474)
    4   at  android.graphics.Bitmap.createScaledBitmap(Bitmap.java:379)
    5   at  android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:498)
    6   at  android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:473)
    7   at  android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336)
    8   at  android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:359)
    9   at  android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:385)
    

    My biggest problem is that I was not able to reproduce the issue locally even on old devices.

    I have implemented lots of things to try to resolve this:

    1. No memory leaks: I made sure there is no memory leaks at all. I removed the views when I dont need them. I also recycled all the bitmaps and made sure the garbage collector is working as it should. And I implemented all the necessary steps in the onDestroy() method
    2. Image size scaled correctly: Before getting the image I get its dimension and calculate the inSampleSize.
    3. Heap size: I also detect the Max Heap size before getting the image and make sure there is enough space. If there is not enough I rescale the image accordingly.

    Code to calculate the correct inSampleSize

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
       {
          // Raw height and width of image
          final int height = options.outHeight;
          final int width = options.outWidth;
          int inSampleSize = 1;
    
          if(height > reqHeight || width > reqWidth)
          {
             if(width > height)
             {
                inSampleSize = Math.round((float) height / (float) reqHeight);
             }
             else
             {
                inSampleSize = Math.round((float) width / (float) reqWidth);
             }
          }
          return inSampleSize;
       }
    

    Code to get the bitmap

        // decodes image and scales it to reduce memory consumption
       private static Bitmap decodeFile(File file, int newWidth, int newHeight)
       {// target size
          try
          {
    
             Bitmap bmp = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), Uri.fromFile(file));
             if(bmp == null)
             {
                // avoid concurrence
                // Decode image size
                BitmapFactory.Options option = new BitmapFactory.Options();
    
                // option = getBitmapOutput(file);
    
                option.inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
                option.inTargetDensity = res.getDisplayMetrics().densityDpi;
    
                if(newHeight > 0 && newWidth > 0) 
                    option.inSampleSize = calculateInSampleSize(option, newWidth, newWidth);
    
                option.inJustDecodeBounds = false;
                byte[] decodeBuffer = new byte[12 * 1024];
                option.inTempStorage = decodeBuffer;
                option.inPurgeable = true;
                option.inInputShareable = true;
                option.inScaled = true;
    
                bmp = BitmapFactory.decodeStream(new FileInputStream(file), null, option);
                if(bmp == null)
                {
                   return null;
                }
    
             }
             else
             {
                int inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
                int inTargetDensity = res.getDisplayMetrics().densityDpi;
                if(inDensity != inTargetDensity)
                {
                   int newBmpWidth = (bmp.getWidth() * inTargetDensity) / inDensity;
                   int newBmpHeight = (bmp.getHeight() * inTargetDensity) / inDensity;
                   bmp = Bitmap.createScaledBitmap(bmp, newBmpWidth, newBmpHeight, true);
                }
             }
    
             return bmp;
          }
          catch(Exception e)
          {
             Log.e("Error calling Application.decodeFile Method params: " + Arrays.toString(new Object[]{file }), e);
          }
          return null;
       }
    

    Code to calculate image size based on Heap size for older devices

    private void calculateImagesSize()
       {
          // only for android older than HoneyComb that does not support large heap
          if(Build.VERSION.SDK_INT < Constants.HONEYCOMB)
          {
             long maxHeapSize = Runtime.getRuntime().maxMemory();
             long maxImageHeap = maxHeapSize - 10485760;
             if(Application.getResource().getDisplayMetrics().densityDpi >= DisplayMetrics.DENSITY_XHIGH)
             {
                maxImageHeap -= 12 * 1048576;
             }
             if(maxImageHeap < (30 * 1048576))
             {
                int screenHeight = Math.min(Application.getResource().getDisplayMetrics().heightPixels, Application.getResource()
                   .getDisplayMetrics().widthPixels);
                long maxImageSize = maxImageHeap / 100;
                long maxPixels = maxImageSize / 4;
                long maxHeight = (long) Math.sqrt(maxPixels / 1.5);
                if(maxHeight < screenHeight)
                {
                   drawableHeight = (int) maxHeight;
                   drawableWidth = (int) (drawableHeight * 1.5);
                }
             }
          }
       }
    

    I think the problem is with the Heap, maybe sometimes the os doesn't allow the application to use the maxheapsize. Also my biggest problem is that I was not able to reproduce the issue, so when I try a fix I have to wait a little to see if users are still getting the error.

    What more could I try to avoid Out of memory issues? Any suggestions would be greatly appreciated. Thanks a lot

  • Y2theZ
    Y2theZ over 11 years
    Hi, thank you for your suggestion but I am actually doing the same thing but with more advanced check. I had the exact code you have but was having a crash. so I modified it to take in consideration more advanced stuff. But the logic in your code is present in mine. Thanks
  • Mehul Ranpara
    Mehul Ranpara over 11 years
    have you used this function?
  • Y2theZ
    Y2theZ over 11 years
    yes I used it first. but I got the OutOfMemoryError so I modified it to what I have today. it reduced the error but it is still appearing
  • Mehul Ranpara
    Mehul Ranpara over 11 years
    what is the size of your image ?
  • Y2theZ
    Y2theZ over 11 years
    usually it is 320x320. but the problem is not with the size of the image because I am scalling it to a very low resolution if needed (When calculating the available heap size)
  • Melllvar
    Melllvar over 9 years
    An OutOfMemoryError cannot be caught
  • Euporie
    Euporie over 9 years
    @Melllvar I tested these code and I have really caught the OOM. Do you mean is not suitable to catch the OOM?
  • Melllvar
    Melllvar over 9 years
    OK, so I'm seeing a lot of conflicting information (e.g. see this post). I myself have had unrecoverable crashes inside try/catch(OutOfMemoryError), but what I'm seeing suggests that this is more or less a matter of luck (e.g. it may be possible, but if you do recover from an error, there is a chance that the error will leave the program in an indeterminate state; therefore the crash will occur no matter what). In any case, your solution may work (I stand corrected) - just not always.
  • Euporie
    Euporie over 9 years
    @Melllvar thanks, I need to learn more about the differences betweeen exception and error, and I have edited my post.
  • Yoann Hercouet
    Yoann Hercouet over 9 years
    You can still get the exception if the image provided is very big.
  • itsrajesh4uguys
    itsrajesh4uguys about 9 years
    Thanks @MuhammedRefaat...:)
  • Avi Levin
    Avi Levin over 8 years
    I don't like the System.gc() solution. try to find why it's happening, look for memory leak and so one.
  • Roman Nazarevych
    Roman Nazarevych over 8 years
    why not to use just o2.inSampleSize = Math.max(o.outHeight,o.outWidth)/imageMaxSize; instead of while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT) scale*=2; According to documentation "Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2." so android will do the rounding automatically.
  • FindOut_Quran
    FindOut_Quran over 8 years
    OutOfMemory most of time never gets catched. Just crashes the app.
  • Abhijit Chakra
    Abhijit Chakra over 7 years
    Here you will lose quality