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:
onDestroy()
methodinSampleSize
.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
Choose the most appropriate decode method based on your image data source. These methods attempt to allocate memory for the constructed bitmap and therefore can easily result in an OutOfMemory exception. Each type of decode method has additional signatures that let you specify decoding options via the BitmapFactory.
A bitmap is a type of memory organization or image file format used to store digital images. The term bitmap comes from the computer programming terminology, meaning just a map of bits, a spatially mapped array of bits.
You can use eraseColor on bitmap to set its color to Transparent. It will useable again without recreating it.
Caution: You should use recycle() only when you are sure that the bitmap is no longer being used. If you call recycle() and later attempt to draw the bitmap, you will get the error: "Canvas: trying to use a recycled bitmap" . The following code snippet gives an example of calling recycle() .
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; }
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; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With