Bitmaps in Android
August 3, 2011 § Leave a Comment
If you work with Bitmaps in Android (pre Honeycomb) it is extremely easy to receive
OutofMemoryError: bitmap size exceeds VM budget
As you know all application have memory (heap size) limits. For some devices it is 16Mb, for others – 32 or 64Mb.
16Mb is not so much but in most cases (until you load 3264×2448 ARGB8888 bitmap) is enough for application. But why working with even small bitmaps OutOfMemory error occurs.
The reason of the error is Bitmap instances are stored not in managed heap as all other objects but in native heap. And memory limit includes the sum of managed and native heap sizes. And at least one reason why native memory is not called “managed” is a garbage collector can’t remove objects from it. You must do it by your own by calling recycle() method of your bitmap instance.
The recycle() method cleans the memory occupied by the object in native heap. It is done immediately after calling recycle(). And after that you shouldn’t use bitmap instance anymore. Overwise you will get
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap
Here is snippet with example how to recycle bitmaps:
Bitmap originalBitmap = BitmapFactory.decodeFile(path, options); Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, width, height, true); originalBitmap.recycle();
In the example we decoded bitmap and then scaled it. But actually we have 2 bitmap objects in native heap. So we should recycle the firs on (originalBitmap).
But if you want to load really big image you can scale it during decoding and safe plenty of memory in the heap.
First of all you should know the real size of the image. You can do it by:
BitmapFactory.Options preOptions = new BitmapFactory.Options(); preOptions.inJustDecodeBounds = true; Bitmap originalBitmap = BitmapFactory.decodeFile(path, preOptions); int width = preOptions.outWidth; int height = preOptions.outHeight;
At this snippet we don’t actually create object in heap. We set inJustDecodeBounds flag for BitmapFactory.Options instance. This flag means that image data will be retrieved without creating the bitmap instance (and at this moment originalBitmap is null). After that we get actual width and height of the image.
Then you can set scale parameters to BitmapFactory.Options instance. But this scale parameter should be multiple of 2:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inTempStorage = new byte[16 * 1024]; options.inPreferredConfig = Config.RGB_565; int scale = preOptions.outHeight/height; int sizeSample = 2*(scale/2 + 1); options.inSampleSize = sizeSample; originalBitmap = BitmapFactory.decodeFile(path, options);
Here we calculated the scale that is multiple of 2 and close to the size we want to have. Then we set it to options.inSampleSize and decode the file. Besides that we set temp storage for decoding (16K) as well as RGB565 config. The last reduces decoded bitmap size 2 times relative to ARGB8888 config.