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

Solution 1:

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

Solution 2:

Please see this for a guide on loading large Bitmaps more efficiently:

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

A 400 KB image file can easily take up 5-10 MB of RAM.

Solution 3:

The size of the file on disk doesn't necessarily coincide with the size of the file in memory. Chances are likely that the file is compressed, which they won't be when decoded. You need to account for that in your calculation.

Multiply the size of the image (width x height) by the color depth of the image to get the in-memory size of the image.

Solution 4:

Basically you can resolve your issue by trying to scale your Bitmap and you'll see memory consumption reduced. To do it you can copy he method shown here.

Also, there is a dedicated page at Android Developeres that could help you understand better how to load large Bitmaps. Take a look at the official documentation.

Solution 5:

While the above answers are obviously correct, a better practice is also to set explicitly the ImageView's bitmap/src property to null, when they're no longer used, mostly when your activity is being destroyed. Any other heavy duty resources( large text, audio, video) etc. may also be nullified. This ensures that the resources are freed instantly, and not wait for the GC to collect.