I have an ArrayList of objects that each of which has a reference to a bitmap.
if I use ArrayList.remove or ArrayList.clear, to remove an object or all of them respectively,
do I need to set the reference to the bitmap to null first or does the remove/clear does it?
I ask this for memory concern, of course.
When you want to clear memory of a bitmap object you should use it recycle method.
bitmap.recycle()
Thats how the memory associated with the bitmap is cleared. As additional phone memory is used to store information of the bitmap pixel values which will be cleard using the recycle function.
Related
I have an object that overwrites the Application object. In it, I have a member variable which is a LongSparseArray where the key is some identifier of type long and the value is an object with 2 member variables: a Bitmap and a long which is used as a timestamp.
This is my global image cache. Occasionally, a function is ran that looks at the timestamps and ages things that are over an hour old.
By "age" I mean that it removes that entire entry from the LongSparseArray.
Here is my question:
Suppose I have an Activity with a ListView. Each row in the ListView has an ImageView that is populated with an image from the cache.
Bitmap image = ((MyApp)getApplicationContext()).getImage(id);
holder.imgImage.setImageBitmap(image);
Now, suppose the user clicks some button which takes them to a new Activity. While on this new Activity, the image previously assigned to a row in the ListView in the previous Activity ages.
So, to recap, that Bitmap key/value entry now no longer exists in the global LongSparseArray.
Is that Bitmap really able to be reclaimed by Java? Isn't it still being referred to by the ImageView in the ListView of the previous Activity? Assuming, of course, that Android hasn't reclaimed the memory used by that Activity.
The reason I'm asking about this is my previous aging function would also call .Recycle() on the Bitmap. In this scenario, when the user hit the back button and returned to the previous Activity which was using that Bitmap, the application would crash, presumably because that Bitmap was not only missing from the cache, but also from memory. So I just removed the .Recycle() call.
By the way, once the Bitmap is removed from the cache, and an object with that id shows up on screen again, the application will download the Bitmap again and place it in the cache. If the previous one stayed in memory, you could see how this would present a problem.
Also, does anyone have any ideas for a more effective solution?
What would happen if I set myImageView.setDrawingCacheEnabled(false);?
There are 2 Activities which use this image caching. One is a search screen that displays a list of items (and their images) after the user performs a search. The other is a list of those items the user has then selected to keep.
Issue: Once recycle() method is called on a bitmap, the bitmap should never be used again. If an attempt is made to draw the bitmap, then an exception will be thrown. From docs:
You should use recycle() only when you are sure that the bitmap is no
longer being used. If you call recycle() and later attempt to draw the
bitmap, you will get the error: "Canvas: trying to use a recycled
bitmap".
In this specific case, you have recycled the bitmap, but the ListView item's ImageView has a strong reference to the bitmap. When you return to the Activity, the ListView item attempts to draw the bitmap, hence the exception is thrown.
Bitmap memory management: Prior to Android 2.3.3, the backing pixel data of a bitmap was stored in native memory and bitmap itself in Dalvik memory. Hence to release the native memory, recycle method has to be called.
Here is Bitmap.recycle function definition:
public void recycle() {
if (!mRecycled) {
if (nativeRecycle(mNativeBitmap)) {
// return value indicates whether native pixel object was actually recycled.
// false indicates that it is still in use at the native level and these
// objects should not be collected now. They will be collected later when the
// Bitmap itself is collected.
mBuffer = null;
mNinePatchChunk = null;
}
mRecycled = true;
}
}
Post Android 3.0, the backing pixel data is also stored in Dalvik memory. When the bitmap is no longer required, we need to ensure we don't hold any strong reference to the bitmap, so that it is garbage collected.
Solution: If you are still supporting Android 2.3.3 and lower version, you still need to use recycle to release the bitmap.
You can use reference counting to track whether the bitmap is currently being referenced by the ListView item, so that even it is aged, you don't call recycle on the bitmap.
ListView adapater's getView method is the place where the bitmap is assigned to the ImageView. Here you increment the reference count. You can attach setRecyclerListener to the ListView to know whenever the listview item is put into recycle bin. This is the place you would decrement the reference count of the bitmap. The aging function need to recycle the bitmap only if the reference count is zero.
You can also consider using LruCache for caching, as mentioned in docs.
setDrawingCacheEnabled: By calling this method with true param, the next call to getDrawingCache will draw the view to a bitmap. The bitmap version of view can be rendered on to the screen. Since it is just a bitmap, we cannot interact with it as done with an actual view. Couple of use cases are:
When ListView is being scrolled, the bitmap of the displayed items view is captured and rendered. So that the views being scrolled don't undergo measure and layout pass.
View hierarchy feature in DDMS.
Is that Bitmap really able to be reclaimed by Java? Isn't it still
being referred to by the ImageView in the ListView of the previous
Activity? Assuming, of course, that Android hasn't reclaimed the
memory used by that Activity.
The Bitmap is stilled used in the ListView (a strong reference) so dalvik can't reclaim its memory.
Apparently you can't call recycle on the Bitmap or bad things will happen(app crash, e.g.).
What would happen if I set myImageView.setDrawingCacheEnabled(false);?
If you disable drawing cache, every time your view needs to be redrawn, the onDraw method will be called.I'm not very familiar with ImageView , you can go and read its source for a deep understanding.
(Note: the usage of drawing cache is different when hardware accerleration is enabled/disabled, here I just assume you're using software rendering).
For the solution, you can try the following:
when the Bitmap cache become stale, you remove it from the cache array(and then you app will try to get a new one, I think).
In ListView.getView, you can check whether currently used Bitmap ages. It should be easy because you know the timestamp when you call setImageBitmap the first time and the latest timestamp. If they are not same, you call setImageBitmap again using the new Bitmap and the old one will be reclaimed.
Wish this helps.
Regarding, "Also, does anyone have any ideas for a more effective solution?"
The Picasso library would help solve the problems you are facing http://square.github.io/picasso/
Picasso is "A powerful image downloading and caching library for Android"
"Many common pitfalls of image loading on Android are handled automatically by Picasso:
Handling ImageView recycling and download cancelation in an adapter.
Automatic memory and disk caching."
Do Bitmaps automatically get recycled when an activity finish()?
Or do we have to call recycle() the bitmaps in onDestroy()?
In the Pre Android 3.0 you need to recycle it, because the bitmap is stored in the native heap.
as long as you're not referencing them by any still alive object: they get garbage collected by the GC without any need to further interaction.
On Pre-honeycomb device
Bitmap Object reference allocated on Dalvik Heap
Pixel information stored on Native layer
recycle() or finalizer() needed to restore memory
On Post honeycomb its standard like any java object
First, I use AsyncTask to download bitmap and contain bitmap into array.
myBitmap[0] = task.execute().get();
My first question is, if I recycle() my array of Bitmap and download by using
ASyncTask again when start Activity, This would cause any problems to memory or not.
Because if I download all Bitmap into my device, errors would occur surely. That's why I
have to download and remove them again and again.
And second question is, If I have some Bitmap as follows:
Bitmap newBitmap = myBitmap[0];
Do I have to recycle() the newBitmap to decrease memory?
Yes, I think you will have to call recycle to decrease memory usage. You could do it
1. when overwriting an existing bitmap location
2. releasing all bitmaps when exiting the app
3. Keeping tabs on number of bitmaps loaded at one time.
I suggest using an LruCache to manage the bitmaps. Helpful when keeping tabs on which bitmap is used most frequently etc.
I have to free memory occupied by bitmaps. So I'm calling recycle() on all of them, when my activities finish.
These bitmaps are always used in drawables, either as background property or source (case of ImageView) property.
But the problem is, when later another activity has a view that also uses these bitmaps, it will show
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap.
What can I do, I can't keep the bitmaps in memory only because maybe the user starts, at some point later, an activity which uses them. How do I tell the bitmaps to free memory and also please be possible to be used later again?
The solutionis to initialize the Bitmap and View using this:
Bitmap b = BitmapFactory.decodeResource(resources, R.drawable.mybitmap);
view.setBackgroundDrawable(new BitmapDrawable(b));
This does not work for XML bitmaps although. But the bitmap should be retrievable from the view.
Having the bitmap instance in memory, it's possible to call recycle() on it to free memory immediatly.
And before recycling the bitmap it has to be ensured that it's not referenced by any view anymore - setting the references of the drawable / view to null. Otherwise the exception will be thrown. In order to do this it's necessary to keep track of the views which are referencing a bitmap and null all the references before recycling it.
So my actual problem was not reusing the bitmap. The problem was recycling the bitmap while is still referenced by a view.
If all you use the Bitmaps for is Drawables and android:source of ImageViews, then why do you have Bitmap objects at all? Android will take care of optimal memory use in those cases and there is no need to call recycle() at all.
You can check if the bitmap is recycled with bitmap.isRecycled(). If this method returns true, you can reinitialize the bitmap. Something like this:
if(bitmap != null && bitmap.isRecycled()) {
//Reinitialize the image
}
else {
//The bitmap is not recycled, you can use it
}
is it reasonable to pass a Drawable or Bitmap across via onRetainNonConfigurationInstance() to save the expense of calling res.getDrawable() again?
You should not pass drawables. Android may choose different drawables from resources after changing orientation.
In my project i am passing only data, which i loaded from internet.
http://developer.android.com/resources/articles/faster-screen-orientation-change.html
Here is written:
Be very careful with the object you pass through onRetainNonConfigurationChange(), though. If the object you pass is for some reason tied to the Activity/Context, you will leak all the views and resources of the activity. This means you should never pass a View, a Drawable, an Adapter, etc.
Sorry for my English =)