In my activity I have
final ImageView img = (ImageView) findViewById(R.id.myimage);
img.setImageDrawable(getdrawable()); //getdrawable() is my own function which returns a drawable
Then this line
img.setImageDrawable(getdrawable()); //this line is
is called many times to replace the drawable on the current imageview.
Will I need to recycle the drawable or will the GC do it for me? and if i do need to recycle then how do i do it?
Drawables, like anything else that uses memory, get marked for garbage collection whenever there are no longer any references pointing to them. If you're running into a situation where memory isn't released as soon as you drop the reference to the previous drawable, well, that's GarbageCollector for you. GC will try to do its work in the most efficient way possible, which means that, from your perspective, it gets to it when it gets to it, and you can't make it go any faster, even if you call the object's dispose() method.
Read this guide for a really good explanation of what's going on under the covers with garbage collection.
Related
I have a game where four images are displayed, the user answers a question and then re-display four different ones. I display images creating a Bitmap and load them from assets folder.
Which is the best practice, call the method removeAllViews() of the view and recreate the images or change pictures with setImageBitmap. Thanks.
Look at this source code: android.widget.ImageView#updateDrawable(Drawable)
Old Drawable will be replaced by the new one. Garbage Collector frees the allocated memory sometime later. Unless you are holding somewhere the reference to the old image (List, Map, etc...).
I need to set a different background to layout based on some event. This was causing OOM exception. I added code to fix this but this is now causing recycled bitmap issue.
Here is the code:
if (change_bg != 0) {
//Garbage collect the current BG.
BitmapDrawable bg = ((BitmapDrawable)llayout.getBackground());
llayout.setBackgroundResource(0);
if (bg != null) {
bg.getBitmap().recycle();
bg = null;
}
System.gc();
llayout.setBackgroundResource(change_bg);
llayout.invalidate();
}
This does not happen the first time but after some 4-5 events randomly.
If I do not call bg.getBitmap().recycle, the OOM exception is encountered.
Can anyone point out the mistake or suggest the correct method to set the layout background
in the same view?
Note:
I removed the android:background="#drawable/bg_initial" in the ;ayout but it had no effect.
The App is using other views with layouts having background image and the problem
is not seen with those views.
Prima facie this does not seem to be memory issue but problem when setting a different
background in the same view. I can implement a different to switch the background image
but this seems overkill. Would be glad if someone offers a simple and real solution.
EDIT :
I added the background bitmap garbage collect code in OnDestroy() and that seems to have
solved the issue.. atleast I am not able to recreate the problem now.
But I am wondering what is the explanation for this. I am not holding the layout or
images in a static variable so I am not sure it was held in memory. I am wondering
what is going on. Can someone explain this?
This might be because the imageView is referring to the image, and before you setting a new background image, you are recycling the previous image (which is still referred by the imageView), and hence its throwing the exception.
Try following:
Get the previous background by BitmapDrawable bg = ((BitmapDrawable)llayout.getBackground());
Now set new background
Garbage collect the previous background.
Please see my answer to solve OOME, it might help you. bitmap size exceeds Vm budget error android
I'm getting drawing cache of the view, that is set as contentView to the Activity. Then I set new content view to the activity and pass that drawing cache to it. But Android recycles my bitmaps and I'm getting this exception:
06-13 01:58:04.132: E/AndroidRuntime(15106): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap#40e72dd8
Any way to fix it? I had an idea to extend Bitmap class, but it's final. Why GC is recycling it?
A bitmap being recycled does not mean it was Garbage Collected, it means Bitmap.recycle() was called on it, on purpose. I'm not familiar with the specific code, but I'm pretty sure that when you call setContentView() with something new, it will tear down the previous views (since it's assumed that you don't want them anymore). It seems to be hitting an ImageView and that calls recycle() on its Bitmap to be a good citizen, which then gives you your error. My guess is that what you're trying to do is not supported.
Problem solved, I just cloned the bitmap to new. I think, exception occurred because the cached view was removed with it's drawing cache.
So I have an APP that loops and continuously draws many png files to a canvas. In the constructor for the thread, for some of the pngs I declare Drawable and some Bitmap handles and assign them (respectively) like so:
Drawablename = context.getResources().getDrawable(R.drawable.pngresource);
mBackgroundImage = BitmapFactory.decodeResource(res, R.drawable.bckgrnd);
Keep in mind I do both methods, MANY times. (even though most of the images are fairly small)
Well... the problem i'm getting is that when trying to start this app on older devices (specifically like the original droid and older) it force closes with the VM budget error.
From research, I've noticed that this seems to be a common problem. (The app runs fine on all newer devices such as droid x, every tablet at best buy, charge, atrix, etc.)
So my question is could I be doing something better?
Is one of these methods of referencing the pngs superior?
Also What exactly is happening here? I need to be able to reference the Image to draw at any time. In other words, at any given instant I could call Draw on one of the handles.
The fixes I've seen for similar problems involve calling the Garbage collector, but would that help me since I would need the Images later anyway? Or is this exclusively a problem with the way i'm referencing the pngs from the drawable folder?
Sorry if this is confusing, i'm a beginner.
If I called System.gc() after every reference would that help, even though the reference is still stored as a Drawable object?
The common fix:
Resize image size, for example: createScaledBitmap()...
Reduce image quality, Config.inSampleSize setting...
Remove all references to Bitmap objects when un-used (setting references to NULL, of course). If you don't do this, System.gc() will does nothing, remember this! and this method call does not guarantee that the memory will be freed right away.
While trying to implement small in-memory cache of Drawables, I learned that to avoid memory leaks after closing activity I need to unbind those Drawables: set their callback to null.
Because maintaining Drawables cached in each activity would require extra code, I tried to unbind them immediately after setImageDrawable(drawable) and I don't see any consequences so far.
This is code from MyImageView class (extends ImageView):
setImageDrawable(drawable);
d.setCallback(null);
In debugger I can clearly see that before first line callback is null, after first line it is set to this imageView, and after that I set it to null again. It is normally shown after that..
Documentation for setCallback (Drawable.Callback cb) states:
Bind a Drawable.Callback object to this Drawable. Required for clients that want to support animated drawables.
Since I don't need animated drawable, I don't see why I shouldn't do this but it bothers me that in several blogs about memory leakage in Android concerning drawables this is done only after activity is done. Question is, why is callback always automatically set when binding to ImageView?
Are there some border conditions where those drawables with callback set to null will cause a problem? Not displaying or NPE?
You should not cache Drawables -- the Drawable object is very stateful, and intended to be used by one and only one owner.
If you want to implement a cache, you should be caching the drawable's constant state.
The constant state is retrieve with this:
http://developer.android.com/reference/android/graphics/drawable/Drawable.html#getConstantState()
(Note this method can return null; not all Drawables have constant state.)
You can later instantiate new Drawables from a constant state with this:
http://developer.android.com/reference/android/graphics/drawable/Drawable.ConstantState.html#newDrawable(android.content.res.Resources)
Also keep in mind that Resources already maintains a cache of Drawables for you, using this facility, so there is no need for you to implement your own cache for any Drawables you are retrieving from Resources.
And if you are making your own Drawables outside of resources, I would strongly recommend making a cache of the underlying data (such as a bitmap downloaded from the network) then trying to mess with the constant state. (And again, definitely don't cache Drawable objects themselves.)