Android bitmap size exceeds VM budget.
My app is getting this error frequently. I have two questions.
Do I need to recycle my about activity (it contains some imageviews and buttons and textViews)?
What is the difference between .recycle(); and between system.gc(); ?
You should always try and recycle Bitmaps afte you have used them.
As far as I understand, you should try and avoid calling system.gc().
Calling recycle() will allow the bitmap object to be garbage collected.
I hope this helps.
I got the same problem while picking images from camera.
I resized the bitmap of image using following code:
Bitmap bitmap = resizeBitMapImage(picturePath, 75, 91);
profilePic.setImageBitmap(bitmap);
private Bitmap resizeBitMapImage(String filePath, int targetWidth,
int targetHeight) {
Bitmap bitMapImage = null;
// First, get the dimensions of the image
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
double sampleSize = 0;
// Only scale if we need to
// (16384 buffer for img processing)
Boolean scaleByHeight = Math.abs(options.outHeight - targetHeight) >= Math
.abs(options.outWidth - targetWidth);
if (options.outHeight * options.outWidth * 2 >= 1638) {
// Load, scaling to smallest power of 2 that'll get it <= desired
// dimensions
sampleSize = scaleByHeight ? options.outHeight / targetHeight
: options.outWidth / targetWidth;
sampleSize = (int) Math.pow(2d,
Math.floor(Math.log(sampleSize) / Math.log(2d)));
}
// Do the actual decoding
options.inJustDecodeBounds = false;
options.inTempStorage = new byte[128];
while (true) {
try {
options.inSampleSize = (int) sampleSize;
bitMapImage = BitmapFactory.decodeFile(filePath, options);
break;
} catch (Exception ex) {
try {
sampleSize = sampleSize * 2;
} catch (Exception ex1) {
}
}
}
return bitMapImage;
}
Related
Here is a piece of code that I used to compress Bitmap:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.out.println("before: " + bmp.getByteCount());
bmp.compress(Bitmap.CompressFormat.JPEG, 80, baos);
System.out.println("baos: " + baos.toByteArray().length);
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap b = BitmapFactory.decodeStream(new ByteArrayInputStream(baos.toByteArray()),
null, options);
System.out.println("after: " + b.getByteCount());
LogCat output:
12-29 11:45:52.638 18042-18042/com.xxx.yyy I/System.out: before: 653760
12-29 11:45:52.678 18042-18042/com.xxx.yyy I/System.out: baos: 13118
12-29 11:45:52.688 18042-18042/com.xxx.yyy I/System.out: after: 1307520
size of baos seems the size of Bitmap after compressing, but why b.getByteCount() return a larger size than bmp before compressing?
BitmapFactory.Options options = new BitmapFactory.Options();
/*
isSampleSize will reduce your bitmap size.
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save
memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the
decoded bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original
and 1/16 the number of pixels. Any value <= 1 is treated the same as 1. 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.
*/
options.inSampleSize = 2;
Bitmap b = BitmapFactory.decodeStream(new ByteArrayInputStream(baos.toByteArray()), null, options);
System.out.println("after: " + b.getByteCount());
I had Same Problem but i solved using Some Method.i used this method in my Camera App after image Captured.i put my code here.Using it you will try.
My Methods :
public static Bitmap decodeSampledBitmapFromByte(Context context, byte[] bitmapBytes) {
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int reqWidth, reqHeight;
Point point = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(point);
reqWidth = point.x;
reqHeight = point.y;
} else {
reqWidth = display.getWidth();
reqHeight = display.getHeight();
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inMutable = true;
options.inBitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Load & resize the image to be 1/inSampleSize dimensions
// Use when you do not want to scale the image with a inSampleSize that is a power of 2
options.inScaled = true;
options.inDensity = options.outWidth;
options.inTargetDensity = reqWidth * options.inSampleSize;
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false; // If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
options.inPurgeable = true; // Tell to gc that whether it needs free memory, the Bitmap can be cleared
options.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.decodeByteArray(bitmapBytes, 0, bitmapBytes.length, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int initialInSampleSize = computeInitialSampleSize(options, reqWidth, reqHeight);
int roundedInSampleSize;
if (initialInSampleSize <= 8) {
roundedInSampleSize = 1;
while (roundedInSampleSize < initialInSampleSize) {
// Shift one bit to left
roundedInSampleSize <<= 1;
}
} else {
roundedInSampleSize = (initialInSampleSize + 7) / 8 * 8;
}
return roundedInSampleSize;
}
private static int computeInitialSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final double height = options.outHeight;
final double width = options.outWidth;
final long maxNumOfPixels = reqWidth * reqHeight;
final int minSideLength = Math.min(reqHeight, reqWidth);
int lowerBound = (maxNumOfPixels < 0) ? 1 :
(int) Math.ceil(Math.sqrt(width * height / maxNumOfPixels));
int upperBound = (minSideLength < 0) ? 128 :
(int) Math.min(Math.floor(width / minSideLength),
Math.floor(height / minSideLength));
if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}
if (maxNumOfPixels < 0 && minSideLength < 0) {
return 1;
} else if (minSideLength < 0) {
return lowerBound;
} else {
return upperBound;
}
}
i think u need to Use My method decodeSampledBitmapFromByte() instead of your
method.you need to put all methods in your code.
Using this Code i get same size After compressing.i put my LogCat here.
Hope it will help You...Enjoy.(:
I am processing up to 1200 images. I optimized it to work from 100 images up to 500 with the help of previous questions found here. Now, this is what I have:
public Bitmap getBitmap(String filepath) {
boolean done = false;
int downsampleBy = 2;
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filepath, options);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
while (!done) {
options.inSampleSize = downsampleBy++;
try {
bitmap = BitmapFactory.decodeFile(filepath, options);
done = true;
} catch (OutOfMemoryError e) {
// Ignore. Try again.
}
}
return bitmap;
}
This function is called in a loop, and it goes really fast until it hits the 500th image. At this point it slows down, until it finally stops working at around the 600th image.
At this point I don't know how else to optimize it to make it work. What do you think is happening and how can I fix it?
EDIT
// Decode BItmap considering memory limitations
public Bitmap getBitmap(String filepath) {
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filepath, options);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
options.inSampleSize= calculateInSampleSize(options, 160, 120);
return bitmap = BitmapFactory.decodeFile(filepath, options);
}
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) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Made the changes of the accepted answer. Using the function from Google's tutorials to get the correct sample size. Added largeHeap in the manifest and only calling System.gc() once before I loop through all the images.
First of all, you should never expect to catch an Error. Described here: Java documentation An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
There is some help about loading bitmaps: Android Developers | Loading large bitmaps
You can get some more memory by declaring the largeHeap="true" attribute in your Application Manifest.
And also the System.gc() call might help freeing some unused memory, but I won't really rely on that call.
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:
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
Image size scaled correctly: Before getting the image I get its dimension and calculate the inSampleSize.
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
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;
}
}
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.
I wrote a summary of suggestions in another StackOverFlow question: Android: BitmapFactory.decodeStream() out of memory with a 400KB file with 2MB free heap
actually the problem is with the development os. In android unlike iOS , google people develop this based on camera resolution. Bitmaps take up a lot of memory, especially for rich images like photographs.Different cameras captures images with different pixels(different mobiles have different camera pixel capacity). Here in android based on that pixels only the captured image will take memory. so obviously a high resolution image will not uploaded by a phone with low pixel capacity.
In android os allocates utmost 16MB to every application. If the uploaded image takes more than this then java.lang.OutofMemoryError: bitmap size exceeds VM budget will occur and application crashes.
refer this
http://developer.android.com/training/displaying-bitmaps/index.html
If u want to avoid OOM, u can catch OOM and increase the sampleSize until the image can be resolved:
private Bitmap getBitmapSafely(Resources res, int id, int sampleSize) {
// res = context.getResources(), id = R.drawable.yourimageid
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inSampleSize = sampleSize;
try {
bitmap = BitmapFactory.decodeResource(res,
id, options);
} catch (OutOfMemoryError oom) {
Log.w("ImageView", "OOM with sampleSize " + sampleSize, oom);
System.gc();
bitmap = getBitmapSafely(res, id, sampleSize + 1);
}
return bitmap;
}
Hope it helps.
It is not suitable to catch the Error, just a workaround.
I've created small app that works with images from gallery or camera.
It all works fine, but.
On device with small screen and small memory size (HTC Desire) I have some images downloaded in full size from other mobile phone, and they are much larger (8MP camera on that phone).
If I try to load that, for my small camera huge image, it will crash immediately.
So, how to implement some kind of check and to downsize that image, but still load it properly?
I do scale images down after they are loaded, but this is something that should be done before the crash appears.
Tnx.
InputStream in = null;
try {
in = getContentResolver().openInputStream(data.getData());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// get picture size.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
// resize the picture for memory.
int screenH = getResources().getDisplayMetrics().heightPixels; //800
int screenW = getResources().getDisplayMetrics().widthPixels; //480
int width = options.outWidth / screenW;
int height = options.outHeight / screenH;
Log.w("Screen Width", Integer.toString(width));
Log.w("Screen Height", Integer.toString(height));
int sampleSize = Math.max(width, height);
options.inSampleSize = sampleSize;
options.inJustDecodeBounds = false;
try {
in = getContentResolver().openInputStream(data.getData());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// convert to bitmap with declared size.
Globals.INSTANCE.imageBmp = BitmapFactory.decodeStream(in, null, options);
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
you can avoid load the bitmap in memory just setting
inJustDecodeBounds = true
inJustDecodeBounds will allow you to decode only the bounds of the image without decode it. Given height and width of your bitmap you can downsampling it using.
inSampleSize
as the doc stays:
If set to a value > 1, requests the decoder to subsample the original
image, returning a smaller image to save memory.
int tmpWidth = bitmapWidth;
int tmpHeight = bitmapHeigth;
int requiredSize = ...
while (true) {
if (tmpWidth / 2 < requiredSize
|| tmpHeight / 2 < requiredSize)
break;
tmpWidth /= 2;
tmpHeight /= 2;
ratio *= 2;
}
EDIT: for a 32 bit Bitmap the memory required is width * height * 4
I am initially showing an empty view. When I click on the "Add Image" button it shows a group of images in a grid then I click on an image. I am placing image in the view by adding the ImageView dynamically using Drag Layer. In this way I am adding more Images. Now I want to resize each of the image programmatically. Is it possible? Please provide the code if it is possible.
You can use the following code to resize an image. if you know the image path and required width and height
public static Bitmap resizeBitMapImage1(String filePath, int targetWidth,
int targetHeight) {
Bitmap bitMapImage = null;
// First, get the dimensions of the image
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
double sampleSize = 0;
// Only scale if we need to
// (16384 buffer for img processing)
Boolean scaleByHeight = Math.abs(options.outHeight - targetHeight) >= Math
.abs(options.outWidth - targetWidth);
if (options.outHeight * options.outWidth * 2 >= 1638) {
// Load, scaling to smallest power of 2 that'll get it <= desired
// dimensions
sampleSize = scaleByHeight ? options.outHeight / targetHeight
: options.outWidth / targetWidth;
sampleSize = (int) Math.pow(2d,
Math.floor(Math.log(sampleSize) / Math.log(2d)));
}
// Do the actual decoding
options.inJustDecodeBounds = false;
options.inTempStorage = new byte[128];
while (true) {
try {
options.inSampleSize = (int) sampleSize;
bitMapImage = BitmapFactory.decodeFile(filePath, options);
break;
} catch (Exception ex) {
try {
sampleSize = sampleSize * 2;
} catch (Exception ex1) {
}
}
}
return bitMapImage;
}
Thanks
Deepak