Bruce Lowe
Bruce Lowe

Reputation: 6193

Should I recycle my bitmaps after a resize?

I've got a utility method (below) that resizes a bitmap and gives me back a new version. Since I am doing this with quite a few images & I wanted to reduce the chance of running out of memory, I've recycled the bitmap after usage.

This works fine on almost all devices. However, I've notice on the samsung galaxy tab 3 (10 inch) and the note 10.1 (2014) I'm getting the below stack traces:

java.lang.IllegalArgumentException: Cannot draw recycled bitmaps
at android.view.GLES20Canvas.drawBitmap(GLES20Canvas.java:756)
at android.view.GLES20RecordingCanvas.drawBitmap(GLES20RecordingCanvas.java:104) 

Below is my resize code:

private static Bitmap resizeBitmap(int newWidth, int newHeight, Bitmap bitmapResize) {
    if (bitmapResize == null) {
        return null;
    }

    int width = bitmapResize.getWidth();
    int height = bitmapResize.getHeight();

    float scaleWidth = ((float) newWidth) / width;
    float scaleHeight = ((float) newHeight) / height;

    Matrix matrix = new Matrix();
    matrix.postScale(scaleWidth, scaleHeight);

    Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmapResize,
            newWidth, newHeight, true);

    //SHOULD I DO THIS???
    bitmapResize.recycle();
    return resizedBitmap;
}

I have not figured out why almost all devices work, except those 2 (there may be more). The emulator shows no issues either.

It might be worth noting, not all images give me a "Cannot draw recycled bitmaps" error. Only some. But its consistently the same images.

(In case its of use, my app runs on 2.2 upwards)

Upvotes: 2

Views: 1112

Answers (3)

Bruce Lowe
Bruce Lowe

Reputation: 6193

I've managed to find the solution to my issue. It turns out the original image may be passed back as an optimisation, if the width/height of the resize match the original image.

I imagine on some devices my computations resulted in me trying to resize an image to its existing size. When i recycled the "old" bitmap, i was recyling the resized one too.

The solution is to change my code to say

if (bitmapResize!=resizedBitmap )
    bitmapResize.recycle();

I found my issue covered in this conversation ( which i didn't find in my initial searching for the issue)

https://groups.google.com/forum/#!topic/android-developers/M6njPbo3U0c

Upvotes: 5

Sam
Sam

Reputation: 1662

You may simply use input bitmap reference as output. This way your input bitmap will be overwritten and you won't need to recycle it.

Kind of:

private static Bitmap resizeBitmap(int newWidth, int newHeight, Bitmap bitmapResize) {
if (bitmapResize == null) {
    return null;
}

int width = bitmapResize.getWidth();
int height = bitmapResize.getHeight();

float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;

Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);

return Bitmap.createScaledBitmap(bitmapResize,
        newWidth, newHeight, true);

}

Upvotes: 0

Nir Hartmann
Nir Hartmann

Reputation: 1409

I had a similar issue with on of ma games and from my experience you are doing the right thing. its the right place to recycle the old bitmap and will prevent OutOfMemoryExceptions, keep in mind that this instance of Bitmap will not be usable anymore.

Upvotes: 0

Related Questions