Implementing a global LruCache vs many local LruCaches - android

Several Activitys in my app display images in a ListView where each row of the ListView contains an ImageView.
An example of this would be a search screen where the user searches, gets results, and a picture of each result is shown.
I'm trying to weigh the cost/benefits of implementing a global LruCache vs having each Activity contain its own local LruCache.
Here are my two main problems. Both revolve around the fact that my app is quite large, meaning there are quite a few screens which show these images. Also, my app has the popular side menu way of navigating. Because of this, I could open the menu, tap Activity B, open the menu, tap Activity A, open the menu... etc. and create an Activity stack of ABABABABABABABAB indefinitely.
Global
Won't Activitys with ImageViews using Bitmaps from a global LruCache contain references to these Bitmaps? Suppose the user navigates away from this Activity by clicking some Button. That Activity is now on the Activity stack and still holds references to those Bitmaps. If the LruCache pops a Bitmap off, can that Bitmap really be reclaimed when an ImageView in some Activity on the stack holds a reference to it?
I had previously created my own custom cache. If I called recycle() on a Bitmap and then the user hit the back button to go back to some Activity on the stack that contained an ImageView set to that Bitmap, the app would crash. This is why I believe ImageViews on Activitys on the stack still hold references to Bitmaps.
Local
As I mentioned earlier. My app is quite large, and side menu style of navigation allows the user to create rather large Activity stacks. This would create a lot of LruCaches. And, since you have to declare the size of the LruCache when you initialize it, there wouldn't seem to be any good way of picking a size.
Thoughts? Suggestions?
At this point I think I have to do global, but I don't know how to solve the Activity stack reference problem. I can't imagine this isn't a problem many apps haven't run into. I don't know why I'm not finding information about it.

I'm trying to weigh the cost/benefits of implementing a global
LruCache vs having each Activity contain its own local LruCache.
Global LruCache is the way to move forward, since the same set of bitmaps might be referred in different activity instances. The LruCache can be defined part of Application. If the activity stack can host multiple instances of the same activity (like ABABABAB..), then creating a LruCache locally in that activity will be a bad idea. Very soon Out Of Memory situtation will be reached, as LruCache in each activity instance reserves the defined amount of memory in Dalvik VM. Assume, application memory is 32Mb and you decide LruCache size as 4Mb i.e. 1/8th. Now when we create nearly 7 instances of Activity A, then memory consumption will go to 7*4=28Mb, that itself might trigger OOM.
Won't Activitys with ImageViews using Bitmaps from a global LruCache
contain references to these Bitmaps?
Yes ImageView will also have a strong reference to the bitmap. If the reference is maintained in LruCache, then the reference count will be 2 at that moment.
If the LruCache pops a Bitmap off, can that Bitmap really be reclaimed
when an ImageView in some Activity on the stack holds a reference to
it?
No the bitmap memory can't be reclaimed, as still some ImageView is have a strong reference to it.
At this point I think I have to do global, but I don't know how to
solve the Activity stack reference problem.
LruCache main role is holding strong reference to the bitmap which are more frequently used. So that if there is no strong reference held by any ImageView, the bitmap is prevented from being garbage collected.
Also remember, for Android 2.3.3 and lower versions, you need to implement reference counting mechanism, in order to recycle the bitmaps.

Related

Best practice recycling images android

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...).

What is the efficient way for saving/loading Bitmaps

I'm working on an app that can be used for taking notes, simply a drawing application which is keeping bunch of Bitmap objects. I completed creating those pages(Bitmap) and drawing part but i have trouble with memory management, i dynamically create Bitmap object as full screen (for ex; 1280x720) resolution then working on it.
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
When user starts to create new pages, normally memory keeps growing. Some point i'm getting "Out of memory" error then my application is crashing.
In my application user can navigate between pages with scrolling and can see maximum two pages at a time (partial pages). I wonder if there any existing API for saving those Bitmap objects which is not displayed at that moment and unload from memory and loading those Bitmap objects when need to display. If there is no existing API can you give me some ideas how can i write my own Memory manager, how i must save those Bitmaps and where ?
For example ; User start to create new pages 1-2-3-4 right now i just need show page4 on the screen i don't need to keep page1-2 (page3 could be in memory for smooth transition ?) in memory i need to save it to disk (or encode as PNG ? or any better solution ? ) When user start to scrolling up that means i need to show page3-page4 at the same time (Maybe i also need to load page2 at this moment to Memory for smooth scrolling), so my app works as like that.
You should use multi threading methods and associate them with async task. Take a look here https://developer.android.com/training/multiple-threads/run-code.html

how to clear memory when it is full because of using too many backgrounds in android

Hi guys i am new to android and i posted a question a week ago in this link which basically stated that i was getting a java.lang.outofmemory error when i was using a lot of different backgrounds for my activities.
why am I getting errors when I use different backgrounds in xml
So as a new developer I have searched and searched for a solution as to how to clear the memory as i go from activity but none have been clear or precise. Then i stumbled across this site http://androidactivity.wordpress.com/2011/09/24/solution-for-outofmemoryerror-bitmap-size-exceeds-vm-budget/
which described exactly what i was going through except they use 10 activities and i am only using 4. However when i implemented his code it my project i ended up with null pointer exceptions and after fiddling with his code I ended up back were i started with the same out of memory error.
So can anybody direct me to someone who can show me how to have as many backgrounds as i want with out running out of memory. Or does android as great as it is does not let you simply use more than a certain amount of backgrounds? help?
It's not that there is a limit on the amount of backgrounds, but each background image you load is a loaded into memory as a bitmap and held there until the activity is destroyed. If you are opening multiple activities one after another, each background image will need to be held in memory and so eventually you will get an out of memory exception.
If you set a large background image, you will also experience some blocking on the ui thread, while the image is loaded into memory.
One way around this that worked for me was to use an imageloader. This decodes the image off the ui thread, caches it on disk, loads it into memory and if memory is running low, will clear an image from memory and fallback to the disk cache. You may get a slight delay/fade in as the image is loaded but this is not so bad visually, and when loaded once, will load straight away if you go back to that activity.
Check out Picaso Picasso which is really easy to implement and a great api or Universal Image Loader.
My layouts were all RelativeLayouts and the first child (will be behind all other views) was an ImageView with scaleType centercrop and width and height set to match_parent. When each activity loads (onCreate), just grab a reference to the imageview in your layout and set the required background image using your ImageLoader of choice.
The other option is to have multiple copies of your background image in your resources, with each one resized to perfectly fit your resolutions of choice (drawable-mdpi/-hdpi/-xhdpi etc). This way, you ensure you are never loading images that are way bigger than you need to be displayed and your app will be more forgiving in terms of memory consumption.

Android Drawables memory leak

I work with several large drawables and I don't know how to manage memory leaks.
I tracked the heap size of my application and it doesn't stop to grow (as the allocated memory).
It is especially the "byte array (byte[])" type which grows up and never decrease. (in the DDMS Heap view on Eclipse)
My application is composed of one activity which uses fragments.
Those fragments are displaying several large images.
I tried to set drawables callback to null, set drawables to null, clear my volatile cache (which prevent my app from doing too many disk IO) when I pop back a fragment but the heap never decrease.
In fact, each time I call :
Drawable.createFromResourceStream(context.getResources(), value, new FileInputStream(f), f.getName(), opts);
the heap grows up. How can I free memory ?
Thanks !
A memory leak happens when Java finds objects in the memory that are referenced by your code which is preventing the Garbage Collector from freeing this memory. A common cause in Android is referencing the Activity context rather than the Application context. Make sure your context references the Application (i.e. use getApplicationContext rather than using this. Check this video for explanation on Memory leaks and also check this question.
The question seems answered but a post by Romain Guy seems relevant to get more info: Avoiding Memory Leaks.
Apparently, if you (for example) set a drawable as a background image to a text view by using setBackgroundDrawable* (thus attaching the drawable to the view) then change the orientation (destroying the Activity and redrawing the UI) the drawable will still have access to the old activity (after the old activity's destruction), thus creating a memory leak.
*(as a side note - setBackgroundDrawable has been deprecated since API level 16)

Consequences of drawable.setCallback(null);

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.)

Categories

Resources