Drawable vs Single reusable Bitmap better with memory?
Asked Answered
S

2

20

As I understand it (not that I'm correct) Drawables are generally correctly removed from memory when the application is finished with them. Bitmaps however need to be manually recycled, and sometimes even have a special class written to handle them properly. My question is, in regards to memory and leaks, is it more beneficial to simply stick with Drawables like such:

myView.setBackgroundDrawable(getResources().getDrawable(R.drawable.my_image));
myView1.setBackgroundDrawable(getResources().getDrawable(R.drawable.my_image1));
myView2.setBackgroundDrawable(getResources().getDrawable(R.drawable.my_image2));

rather than something like so with Bitmaps:

Bitmap tmpBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image);
myView.setImageBitmap(tmpBitmap);

tmpBitmap.recycle();
tmpBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image1);
myView1.setImageBitmap(tmpBitmap);

tmpBitmap.recycle();
tmpBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image2);
myView2.setImageBitmap(tmpBitmap);
tmpBitmap.recycle();

I've also read of course that you have to be careful about recycle() method on Bitmaps because they can be removed while still in use? It seems like these issues keep popping up in different forms, but I can't really get a straight answer from anyone on the matter. One person says to reuse a Bitmap and recycle after every use, and others say use Drawables and an unbindDrawables() method (this is what I've been using):

private void unbindDrawables(View view) {
    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}

Any applicable insight would be much appreciated though. Thanks

Singultus answered 12/8, 2011 at 18:37 Comment(0)
P
8

I back Romain's proposal, but I'm not sure your question is addressing your actual problem. I don't know how you handle the references to your Views. Maybe you simply have memory leaks in your application? A lot of memory leaks in Android are related to Context. When a Drawable is attached to a View, the View is set as a callback on the Drawable.

TextView myView = new TextView(this);
myView.setBackgroundDrawable(getDrawable(R.drawable.my_bitmap));

In the code snippet above, this means the Drawable has a reference to the TextView which itself has a reference to the Activity (the Context) which in turns has references to pretty much anything depending on your code.

Without looking at more of your code I guess you're on the right track by setting the stored drawables' callbacks to null when an Activity is destroyed.

Perpetrate answered 12/8, 2011 at 22:33 Comment(1)
thanks, very good information. I get my views through findViewById(R.Id.Image_name); btw. Not sure if this somehow leaves a references to the Context or not.Singultus
I
9

Bitmaps do not need to be manually recycled. They are garbage collected just like Drawables and other objects. Similarly you don't need to unbind drawables, except in very specific situations. It seems that you read a lot of misleading information.

Recycling bitmaps and unbinding drawable can be useful in some situations (for instance if your app manipulate large amounts of bitmap data or if you store drawable in a static manner.)

The two examples you show at the beginning of your question are equivalent. If you load a drawable directly, bitmaps will be loaded on your behalf. If you load bitmaps manually and set them on an ImageView, they will be enclosed in drawables on your behalf.

Use the first solution since it's simpler and don't worry about unbinding and other memory management techniques until you actually need them.

Illsorted answered 12/8, 2011 at 18:59 Comment(3)
well the reason I ask is because I do need memory management techniques. I manipulate the images of multiple views on the click of a button, and this is my menu system. It's fine unless I scroll through each menu, then quit, and do it all again. After a few times the app crashes because of "Bitmap exceeds VM limit OOM". Not positive why it does this, but when I use the unbindDrawables() method it doesn't happen. Or at least I haven't been able to recreate it yet.Singultus
You have to be VERY careful pre-Honeycomb about bitmaps. Pre-Honeycomb, bitmaps are allocated in a RAM space that the Java garbage collector cannot reach. This leads to memory leaks. If you are on Honeycomb and later, you are correct.Bind
The fact that bitmaps were allocated on the native heap did not lead to memory leaks. It simply meant you would have to wait for finalizers to run before the bitmap would be freed.Illsorted
P
8

I back Romain's proposal, but I'm not sure your question is addressing your actual problem. I don't know how you handle the references to your Views. Maybe you simply have memory leaks in your application? A lot of memory leaks in Android are related to Context. When a Drawable is attached to a View, the View is set as a callback on the Drawable.

TextView myView = new TextView(this);
myView.setBackgroundDrawable(getDrawable(R.drawable.my_bitmap));

In the code snippet above, this means the Drawable has a reference to the TextView which itself has a reference to the Activity (the Context) which in turns has references to pretty much anything depending on your code.

Without looking at more of your code I guess you're on the right track by setting the stored drawables' callbacks to null when an Activity is destroyed.

Perpetrate answered 12/8, 2011 at 22:33 Comment(1)
thanks, very good information. I get my views through findViewById(R.Id.Image_name); btw. Not sure if this somehow leaves a references to the Context or not.Singultus

© 2022 - 2024 — McMap. All rights reserved.