OutOfMemory (Due to large bitmap) Handling in Android

Here i will explain how you can avoid outOfMemoryError in android, which is very usual when app deals with large size bitmaps. Unfortunately there no any magical number to define this large size. It depends upon various factors like device config, OS version, available RAM ect.

I am assuming that you are already using other memory optimizations techniques like releasing resouces/UI elements in onPause() and assigning them back in onResume() .  So i will mainly focus on bitmap optimization.

Typical format of OOM Error can be like :

06-11 14:51:18.976: E/dalvikvm-heap(19566): Out of memory on a 45349648-byte allocation.
06-11 14:51:18.986: E/AndroidRuntime(19566): FATAL EXCEPTION: main
06-11 14:51:18.986: E/AndroidRuntime(19566): java.lang.OutOfMemoryError
06-11 14:51:18.986: E/AndroidRuntime(19566): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
06-11 14:51:18.986: E/AndroidRuntime(19566): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:596)

Or

OutofMemoryError: bitmap size exceeds VM budget
ERROR/dalvikvm-heap(1923):925200-byte external allocation too large for this process.

Root cause of the issue is that Runtime Memory allocated to an app is limited (and significantly lesser) and app is trying to use memory beyond that limit. So you will need to scale down images to reduce runtime memory consumption.

Here is how you can do it :

1) If you want to set an image from drawable to ImageView, Do not use ImageView.setImageResource(int resId) directly with drawable id. Instead get Scaled down bitmap(if applicable) and set it to imageView. like this:

iv.setImageBitmap(decodeResource(getResources(), R.drawable.big_image));

private static Bitmap decodeResource(Resources res, int id) {
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
for (options.inSampleSize = 1; options.inSampleSize <= 32; options.inSampleSize++) {
try {
bitmap = BitmapFactory.decodeResource(res, id, options);
Log.d(TAG_LOG, "Decoded successfully for sampleSize " + options.inSampleSize);
break;
} catch (OutOfMemoryError outOfMemoryError) {
// If an OutOfMemoryError occurred, we continue with for loop and next inSampleSize value
Log.e(TAG_LOG, "outOfMemoryError while reading file for sampleSize " + options.inSampleSize
+ " retrying with higher value");
}
}
return bitmap;
}

2) If you want to read an image from memory, do not use  BitmapFactory.decodeFile(String pathName), Instead use image, scaled down up-to optimal point. like this :

Bitmap bitmap = decodeFile(filePath);

public static Bitmap decodeFile(String pathName) {
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
for (options.inSampleSize = 1; options.inSampleSize <= 32; options.inSampleSize++) {
try {
bitmap = BitmapFactory.decodeFile(pathName, options);
Log.d(TAG_LOG, "Decoded successfully for sampleSize " + options.inSampleSize);
break;
} catch (OutOfMemoryError outOfMemoryError) {
// If an OutOfMemoryError occurred, we continue with for loop and next inSampleSize value
Log.e(TAG_LOG, "outOfMemoryError while reading file for sampleSize " + options.inSampleSize
+ " retrying with higher value");
}
}
return bitmap;
}

3) If you want to show a large image in small ImageView (eg thumnail), pass small sized bitmap to imageView instead of sending original bitmap and let android scale down automatically based upon UI params like scaleType/width/height.

Here is how you can get scaled down bitmap:

Bitmap bitmap = getResizedBitmap(bitmap, imageView.getWidth(), imageView.getHeight());

private static Bitmap getResizedBitmap(Bitmap bitmap, float maxWidth, float maxHeight) {

float width = bitmap.getWidth();
float height = bitmap.getHeight();
if (width > maxWidth) {
height = (maxWidth / width) * height;
width = maxWidth;
}
if (height > maxHeight) {
width = (maxHeight / height) * width;
height = maxHeight;
}
return Bitmap.createScaledBitmap(bitmap, (int) width, (int) height, true);

}

As you can see,  both the methods in  1 & 2,  try to decode bitmap and whenever face OOM, release resources and retry with increased options.inSampleSize. increment in inSampleSize decrease memory consumption by reducing image size and quality.

I have uploaded a demo project with both OOM prone and OOM proof code here for reference.

Advertisements
Posted in Uncategorized | 1 Comment

Zoom, Pinch, Mark and Save an image in Android

When you want to zoom/pinch an imageView in android, you have plenty of  straight implementation available on web like http://developer.android.com/training/animation/zoom.html. Unfortunately, most of the implementations zoom image from top/left corner and do not keep fingers of user as center point, which is not the desired way of zoom/pinch(for me at-lease). so i found https://github.com/jasonpolites/gesture-imageview  more suitable. Although it is a bit lengthy, but i least bother about that as it is very straight to incorporate in your code. So zoom, pinch and scrolling of imageView was already there.

Addition thing i added here is :

1) Mark/Annotate image at the user touched area on screen.

2) Save this edited image into SD Card.

 

If you are in hurry to get the code, you can download it from here.

At the time of creating bitmap of edited canvas, you may face OutOfMemoryError for some low memory devices. I want to write about OOM handling as well, but in separate thread, because that probably will not be applicable for everyone.

AnnotateImageView#drawView(Canvas canvas) is the method which deals with canvas editing. In my case i wanted dimensions of rectangle to wary in same proportion along with zoom/pinch. if you want to keep it constant, divide all four dimensions by variable scaleAdjust .

I am not sure, which code snippet need explicit explanation. So in case of any query/feedback, please post in as comment. i will revert and will try to incorporate in this post as well.

 

You can download the complete sample from https://github.com/shailendra123/Editable-image-view

Posted in Uncategorized | Leave a comment