Bitmapfactory.Decodestream Out of Memory Despite Using Reduced Sample Size

Android: BitmapFactory.decodeStream() out of memory with a 400KB file with 2MB free heap

Android library is not so smart for loading images, so you have to create workarounds for this.

In my tests, Drawable.createFromStream uses more memory than BitmapFactory.decodeStream.

You may change the Color scheme to reduce memory (RGB_565), but the image will lose quality too:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

Reference: http://developer.android.com/reference/android/graphics/Bitmap.Config.html

You can also load a scaled image, which will decrease a lot the memory usage, but you have to know your images to not lose too much quality of it.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

Reference: http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

To define the inSampleSize dynamically, you may want to know the image size to take your decision:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeStream(stream, null, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;

options.inJustDecodeBounds = false;
// recreate the stream
// make some calculation to define inSampleSize
options.inSampleSize = ?;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

You can customize the inSampleSize according to the screen size of the device. To get the screen size, you can do:

DisplayMetrics metrics = new DisplayMetrics();
((Activity) activity).getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;

Other tutorials:

  • http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
  • http://developer.android.com/training/displaying-bitmaps/index.html

Android getting an OutOfMemoryError

The error is self explanatory, your Image or images are too large, try something with a maximum of 10kb. This will help you save the memory. This error is common if you are testing on emulator, If so then go to that specific emulator device on device manager and click edit then Increase the RAM, and also internal memory if necessary and also the heap size. Well as for me I would just do my testing first on a real device, if the same issue persists then I would have to review the size of my images.

Out of memory error

You are getting OOM Errors because your Bitmap is taking up too much of memory. See if this can help: Handling large Bitmaps. Down sampling the image to half its original size may still result in an image that is just too big for the runtime.

Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

I guess the problem is not in your layout; the problem is somewhere else in your code. And you are probably leaking context somewhere.

Other probable reason is that you must be creating bulky multiple objects while parsing your XML (as you mentioned this occurs the first time when you parse XML). Though Java has auto garbage collection approach, but still you can not completely rely on it. It is a good practice to nullify your collection instance or clear your objects content when you don't need them any more.

But still I have prepared a list of important points which you should remember while dealing with bitmaps on Android.

1) You can call recycle on each bitmap and set them to null. (bitmap.recycle() will release all the memory used by this bitmap, but it does not nullify the bitmap object).

2) You can also unbind the drawables associated with layouts when an activity is destroyed. Try the code given below and also have a look at this link link.

    private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}

// Call this method from onDestroy()

void onDestroy() {
super.onDestroy();
unbindDrawables(findViewById(R.id.RootView));
System.gc();
}

3) You can convert your hashmaps to WeakHashmaps, so that its memory would get released when the system runs low on memory.

4) You can scale/resize all your bitmaps. To scale bitmaps you can try something like this:

    BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap=BitmapFactory.decodeStream(is, null, options);

This inSampleSize option reduces memory consumption.

Here's a complete method. First it reads the image size without decoding the content itself. Then it finds the best inSampleSize value; it should be a power of 2. And finally the image is decoded.

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
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_SIZE=70;

// Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2 >= REQUIRED_SIZE && o.outHeight/scale/2 >= REQUIRED_SIZE)
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;
}

Have a look at this link..

5) You can override onLowMemory() method in an activity which gets a call when entire system runs low on memory. You can release a few resources there.

6) You can make your objects SoftReference or Weakreference, so that they get released in a low-memory condition.

A very common memory leak that I observed is due to the implementation of inner classes and implementing Handler in Activity. This links talks about the same in more detail.

I hope this helps to eliminate your problem.

Strange OutOfMemory issue while loading an image to a Bitmap object

The Android Training class, "Displaying Bitmaps Efficiently", offers some great information for understanding and dealing with the exception `java.lang.OutOfMemoryError: bitmap size exceeds VM budget when loading Bitmaps.



Read Bitmap Dimensions and Type

The BitmapFactory class provides several decoding methods (decodeByteArray(), decodeFile(), decodeResource(), etc.) for creating a Bitmap from various sources. 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.Options class. Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidth, outHeight and outMimeType. This technique allows you to read the dimensions and type of the image data prior to the construction (and memory allocation) of the bitmap.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.



Load a scaled-down version into Memory

Now that the image dimensions are known, they can be used to decide if the full image should be loaded into memory or if a subsampled version should be loaded instead. Here are some factors to consider:

  • Estimated memory usage of loading the full image in memory.
  • The amount of memory you are willing to commit to loading this image given any other memory requirements of your application.
  • Dimensions of the target ImageView or UI component that the image is to be loaded into.
  • Screen size and density of the current device.

For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.

To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize to true in your BitmapFactory.Options object. For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration of ARGB_8888). Here’s a method to calculate a sample size value that is a power of two based on a target width and height:

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

Note: A power of two value is calculated because the decoder uses a
final value by rounding down to the nearest power of two, as per the
inSampleSize documentation.

To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSizevalue andinJustDecodeBoundsset tofalse`:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}

This method makes it easy to load a bitmap of arbitrarily large size into an ImageView that displays a 100x100 pixel thumbnail, as shown in the following example code:

mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

You can follow a similar process to decode bitmaps from other sources, by substituting the appropriate BitmapFactory.decode* method as needed.



Related Topics



Leave a reply



Submit