patternjavaMinor
Handling large images efficiently (to avoid OutOfMemoryError)
Viewed 0 times
handlingefficientlyoutofmemoryerroravoidlargeimages
Problem
After lots of attempts thanks to all the Android enthusiasts, I've found a method to handle large images efficiently in Android Applications in order to get rid of
```
Bitmap bitmap1;
iv1 = (ImageView) findViewById(R.id.iv1);
iv2 = (ImageView) findViewById(R.id.iv2);
iv3 = (ImageView) findViewById(R.id.iv3);
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.image1);
BitmapFactory.decodeResource(getResources(), R.drawable.image2);
BitmapFactory.decodeResource(getResources(), R.drawable.image3);
int srcWidth = o.outWidth;
int srcHeight = o.outHeight;
int dstWidth = 600;
int dstHeight = 450;
int inSampleSize = 1;
if (srcHeight > dstHeight || srcWidth > dstWidth) {
final int halfHeight = srcHeight / 2;
final int halfWidth = srcWidth / 2;
while ((halfHeight / inSampleSize) > dstWidth
&& (halfWidth / inSampleSize) > dstWidth) {
inSampleSize *= 2;
}
}
o.inSampleSize = inSampleSize;
o.inJustDecodeBounds = false;
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image1, o);
iv1.setImageBitmap(bitmap1);
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image2, o);
iv2.setImageBitmap(bitmap1);
Toast.makeText(getApplicationContext(),
"DEcoded", Toast.LENGTH_LONG).show();
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image3, o);
iv3.setImageBitmap(bitmap1);
@Override
protected void onDestroy() {
bitmap1.recycle();
super.onDestroy();
Toast.makeText(getApplicationContext(),
"Bitmap Destroyed", Toast.LENGTH_LONG).sh
OutOfMemoryError. But I really need to know is this the correct formal way of doing this? Do I need to add some more, or should any changes be made? Please comment.```
Bitmap bitmap1;
iv1 = (ImageView) findViewById(R.id.iv1);
iv2 = (ImageView) findViewById(R.id.iv2);
iv3 = (ImageView) findViewById(R.id.iv3);
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.image1);
BitmapFactory.decodeResource(getResources(), R.drawable.image2);
BitmapFactory.decodeResource(getResources(), R.drawable.image3);
int srcWidth = o.outWidth;
int srcHeight = o.outHeight;
int dstWidth = 600;
int dstHeight = 450;
int inSampleSize = 1;
if (srcHeight > dstHeight || srcWidth > dstWidth) {
final int halfHeight = srcHeight / 2;
final int halfWidth = srcWidth / 2;
while ((halfHeight / inSampleSize) > dstWidth
&& (halfWidth / inSampleSize) > dstWidth) {
inSampleSize *= 2;
}
}
o.inSampleSize = inSampleSize;
o.inJustDecodeBounds = false;
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image1, o);
iv1.setImageBitmap(bitmap1);
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image2, o);
iv2.setImageBitmap(bitmap1);
Toast.makeText(getApplicationContext(),
"DEcoded", Toast.LENGTH_LONG).show();
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image3, o);
iv3.setImageBitmap(bitmap1);
@Override
protected void onDestroy() {
bitmap1.recycle();
super.onDestroy();
Toast.makeText(getApplicationContext(),
"Bitmap Destroyed", Toast.LENGTH_LONG).sh
Solution
This looks like a bug:
You probably meant this way:
In
I'm not sure this is necessary at all, and I cannot find the relevant documentation. If it's really necessary to manually recycle all Bitmaps like this, then you need to store the references of all the Bitmaps you decoded, so that you can recycle on in
This looks a bit like magic, though mostly not your fault:
If I understand the docs correctly, you set
I suspect that if you call
Also in the docs, if decoding fails, then
This pattern is repeated 3 times:
It would be good to extract this to a private method:
Which you can call with:
If indeed you need to recycle the Bitmaps in
while ((halfHeight / inSampleSize) > dstWidth
&& (halfWidth / inSampleSize) > dstWidth) {You probably meant this way:
while ((halfHeight / inSampleSize) > dstHeight
&& (halfWidth / inSampleSize) > dstWidth) {In
onDestroy you call bitmap1.recycle(). One problem with that is bitmap1 might be null. The bigger problem is that you try to decode 3 bitmaps earlier, and bitmap1 will point to the last one, so you will recycle only the last one, neglecting the first two. I'm not sure this is necessary at all, and I cannot find the relevant documentation. If it's really necessary to manually recycle all Bitmaps like this, then you need to store the references of all the Bitmaps you decoded, so that you can recycle on in
onDestroy.This looks a bit like magic, though mostly not your fault:
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.image1);
BitmapFactory.decodeResource(getResources(), R.drawable.image2);
BitmapFactory.decodeResource(getResources(), R.drawable.image3);
int srcWidth = o.outWidth;
int srcHeight = o.outHeight;If I understand the docs correctly, you set
o.inJustDecodeBounds = true so that you can run BitmapFactory.decodeResource(...) without allocating memory for images, so that you can query o.outWidth for image dimensions. If indeed such magic is going on, some comments would be nice.I suspect that if you call
BitmapFactory.decodeResource(...) 3 times, o.outWidth will have the width of only the last image. So you might want to rework this, extracting the main processing to a private method, and running it for each image separately.Also in the docs, if decoding fails, then
o.outWidth will be set to -1, and you might want to add some checks to handle that case gracefully.This pattern is repeated 3 times:
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image1, o);
iv1.setImageBitmap(bitmap1);It would be good to extract this to a private method:
private void setImageBitmap(int viewId, int imageId, BitmapFactory.Options options) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), imageId, options);
ImageView view = (ImageView) findViewById(viewId);
view.setImageBitmap(bitmap);
}Which you can call with:
setImageBitmap(int R.id.iv1, int R.drawable.image1, o);
setImageBitmap(int R.id.iv2, int R.drawable.image2, o);
setImageBitmap(int R.id.iv3, int R.drawable.image3, o);If indeed you need to recycle the Bitmaps in
onDestroy, then the new setImageBitmap helper is a good place to add the decoded Bitmap to a collection.Code Snippets
while ((halfHeight / inSampleSize) > dstWidth
&& (halfWidth / inSampleSize) > dstWidth) {while ((halfHeight / inSampleSize) > dstHeight
&& (halfWidth / inSampleSize) > dstWidth) {BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.image1);
BitmapFactory.decodeResource(getResources(), R.drawable.image2);
BitmapFactory.decodeResource(getResources(), R.drawable.image3);
int srcWidth = o.outWidth;
int srcHeight = o.outHeight;bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.image1, o);
iv1.setImageBitmap(bitmap1);private void setImageBitmap(int viewId, int imageId, BitmapFactory.Options options) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), imageId, options);
ImageView view = (ImageView) findViewById(viewId);
view.setImageBitmap(bitmap);
}Context
StackExchange Code Review Q#61439, answer score: 4
Revisions (0)
No revisions yet.