8
\$\begingroup\$

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 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).show();
    }
\$\endgroup\$
4
\$\begingroup\$

This looks like a bug:

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.

|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.