Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Cannot draw recycled bitmaps" if I invoke recycle() inside onDestroy()

Tags:

I have two activities: MainActivity and Activity2.

The MainActivity simply open the seconds one through Intent.

To return to MainActivity from the Activity2 I press the "back" button.

When I do these steps, the App crashes:

  • open the App: MainActivity appears
  • start the Intent: the Activity2 appears
  • press the "back" button: the MainActivity appears
  • start the Intent: my App crashes because of this error:

    IllegalArgumentException: Cannot draw recycled bitmaps

MainActivity.java:

Intent intent = new Intent(this, Activity2.class);
startActivity(intent);

Activity2.java:

@Override
public void onBackPressed() {
    super.onBackPressed();
}

@Override
protected void onDestroy() {
    super.onDestroy();

    for(Map.Entry<Integer, ImageView> entry : mapImageViews.entrySet()) {
        ImageView imageView = entry.getValue();
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            if(bitmap != null) {
                bitmap.recycle();
            }
            bitmapDrawable = null;
            bitmap = null;
        }
        imageView.setOnClickListener(null);
        imageView.setImageDrawable(null);
        imageView.setImageBitmap(null);
        imageView = null;
        drawable = null;
    }
    mapImageViews.clear();
    mapImageViews = null;
}

Since the application uses high resolution images (already adapted with BitmapFactory and inSampleSize), to avoid memory leaks I invoke recycle() in the onDestroy() method.

As I learned by reading lots of SO answers and on the Web, calling recycle() on bitmap let they to be garbage collected early.

But many other posts advise against invoking recycle(), or at least advise doing it only when you are sure that the bitmap is no longer needed in the Activity, that is in the onDestroy() method.

Now I'm a little worried about what I've learned about it because if I remove the recycle() the error no longer happens.

The error occurs on a device with Android 4.4.2, but it does not occur on a device with Android 6.0 and on Nexus 7 (Android 5.1.1).

  • Is the problem about the activities' stack?
  • Does the GC is trying to free the bitmap's memory too late? In case of this, how to definitively destroy the Activity and ALL its contents?
  • There is any difference between these two Android versions?
  • Or there is something I'm missing/wrong?
like image 212
user2342558 Avatar asked Sep 04 '19 17:09

user2342558


2 Answers

try changing your onDestroy method like below

@Override
protected void onDestroy() {
    for(Map.Entry<Integer, ImageView> entry : mapImageViews.entrySet()) {
        ImageView imageView = entry.getValue();
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            if(bitmap != null) {
                bitmap.recycle();
            }
            bitmapDrawable = null;
            bitmap = null;
        }
        imageView.setOnClickListener(null);
        imageView.setImageDrawable(null);
        imageView.setImageBitmap(null);
        imageView = null;
        drawable = null;
    }
    mapImageViews.clear();
    mapImageViews = null;

    super.onDestroy();
}

Also see this: How to recycle and reuse images in an effective way.

like image 148
Rahul Khurana Avatar answered Oct 13 '22 01:10

Rahul Khurana


According to the documentation for recycle

The bitmap is marked as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing. This operation cannot be reversed, so it should only be called if you are sure there are no further uses for the bitmap.

I cant see how you are assigning your bitmaps to your ImageView, but I assume you are trying to reuse the bitmaps when you start the intent again after they have been recycled. I only ran into the exception if I was using android:src=. If I set the ImageView bitmap using the following in oncreate, it ran fine on all targets you listed without throwing an exception.

imageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.lake_park));

I highly recommend using glide to work with images. https://github.com/bumptech/glide

like image 43
Matthew Williams Avatar answered Oct 13 '22 01:10

Matthew Williams