I am finding that performance degrades after one or more screen rotations, and I presume that this is likely to be because an App's main Activity is destroyed and recreated each time the screen is rotated and that my app must be leaking memory when that happens.
I have read that, contrary to what one might expect, not all the objects created by an app's main Activity (or in classes called by that Activity) are destroyed when the activity is destroyed. Specifically, I think I have read (although I can't now find where) that if the View uses a large bitmap member object then the Activity's onDestroy() method should be over-ridden and the bitmap should be explicitly recycled.
Are there other objects that need to be destroyed or removed when the Activity is destroyed? What about Listeners? Is there a comprehensive tutorial or guide on this subject?
Is there a comprehensive tutorial or
guide on this subject?
Not really.
Are there other objects that need to
be destroyed or removed when the
Activity is destroyed? What about
Listeners?
Bitmaps are unusual, in part because they use memory outside of the 16MB heap, if I understand the byzantine Android memory model correctly.
Beyond large bitmaps, the biggest thing you really need to worry about are things that prevent normal garbage collection from working. Anything that holds onto the activity, directly or indirectly, from a static context will keep the activity from being garbage collected. Examples include:
static data members on classes (e.g., you rig up your own listener framework with one of your services, so your service holds onto a listener which holds onto your activity)
threads (e.g., you manually fork a background thread and do not terminate it)
Note that putting android:configChanges="orientation" in the Manifest prevents the Activity from being destroyed when the screen is rotated. So I no longer need to worry about whether I need to destroy or remove individual bitmaps or other objects! (Thanks to Ribo for pointing this out on another thread.)
Related
I have an AsyncTaskLoader that does some background task. While doing this task, it needs to access some views. (No I can't just get the values of the views beforehand - they are custom views, with some magic stuff attached to it - please just accept this)
However, this is causing a context leak because the AsyncTaskLoader is holding a reference to a Context object.
Question 1) Are context leaks that bad? (my loader runs only for 100ms - 200ms)
Question 2) Is there a way to hold a view without a context leak (I'm pretty sure not so this is just wishful thinking)
It's not that bad, right? Like the old views are garbage collected a fraction of a second later.- that's the only side effect, right?
For the record the view I need a reference to is CropImageView from https://github.com/ArthurHub/Android-Image-Cropper and the method I need to call from inside the Loader is view.getCroppedImage(width, height, CropImageView.RequestSizeOptions.RESIZE_INSIDE);
(no I can't use the prepackaged async versions of getCroppedImage because of reasons)
Yes, even minor leaks are dangerous because you'll never know how much memory it might leak. In your example, when any of the background tasks is holding the reference to context, it won't allow context to be garbage collected thereby leaving a wasted memory. This is fine for small objects but giant objects like context, Bitmaps holds tons lot of memory without letting it collected by GC.
Assume, you started a Loader from Activity A,
A -> x memory ( x ~ very large memory comparatively )
While still, the loaderis in progress you changed the orientation of mobile, which creates a new instance of Activity A with the same amount of memory x. Ideally old instance of Activity A should be garbage collected but GC can't recollect old instance memory as it is Strongly referred to a background task.
Hence, you need to take care of leaks while performing background tasks, this can be done in two ways -
either cancel the loader on destroying the activity instance (or) pass weak reference of your context to a loader.
Yes context leaks is bad,we have to avoid it manually.AsyncWork holds the reference to the Context, the Context cannot be GCed until the task is complete: the Context memory leaks. There are two solutions:
1.Use a context that is long-lived, anyway, the Application context.
2.Tie the life of the async task to the life of the context to which it hold a reference: cancel it in onPause().
I have a Fragment which has a RecyclerView.
In this RecyclerView, I may occasionally download and display images (loaded with Glide into ImageView.
So when I open the Fragment, used memory may sometimes jump from around 30MB to around 100MB or even more.
After the Activity that is holding the Fragment is finished, the memory does not free up. It stays the same as before.
I checked Glide documentation and apparently we don't have to worry about freeing up Bitmaps in RecyclerView. This is a huge issue, because app often crashes due to OOM because of this.
How should I correctly handle freeing up memory when Fragment is removed?
Edit: another observation
Another thing I noticed is that if I finish the Activity and then start the same Activity again. Memory will jump back down for a moment and then back up to 100MB, which leads me to believe that the memory is cleared before launching the Fragment again.
Garbage Collection is sometimes a painful issue in Android.
Most developers fail to consider this issue and just keep developing without any sense of resource allocation.
This will of course cause memory problems such as leaks, OOM and unnecessary resource binding. There is absolutely no automatic way to free up memory. You can not, under any circumstances, rely solely on the Garbage Collector
Whenever you pass the Fragment's or Activity's onDestroy() method, what you can and should do is erase any construct that shall no longer be required in the application. You can do the following :
Avoid anonymous instances of listeners. Create listeners and destroy them when you no longer need them.
Set all the listeners (be them click, longclick, etc) to null
Clear all variables, arrays. Apply the same procedure to all the classes and subclasses contained inside the Activity/Fragment
Set the variable to null whenever you perform any of the previous steps on that given class (applies to all variables)
What I ended up doing was creating an interface like
public interface clearMemory(){
void clearMemory();
}
and implementing it on every class, be it Activity, Fragment or a normal class (includes adapters, custom views, etc).
I would then call the method whenever the class was to be destroyed (because the app was being destroyed or whenever I felt need to do so. Careful not to dispose in normal runtime)
#Override
public void onDestroy(){
clearMemory();
}
public void clearMemory(){
normalButtonOnClickListener = null;
normalButton.setOnClickListener(null);
normalButton = null;
myCustomClass.clearMemory(); // apply the interface to the class and clear it inside
myCustomClass = null;
simpleVariable = null;
...
}
By implementing this in a systematic way, my applications' memory management has become easier and leaner. One can then then know/control exactly how and when the memory is disposed.
This is adding on to Ricardo's answer.
You can add the following code to initiate garbage collection in Android:
Runtime.getRuntime().gc();
Note: Call this function after you've made all local variables null. Execution of this code doesn't guarantee that the system will garbage collect on your app, it merely hints that it might be a good time to do it.
I've used this in all my activities' onDestroy(), and it always seems to work when I want it to.
Give it a try, it might help you out.
1)It is considered a good tactic to recycle all bitmaps and data at activity's OnStop method.
2)It's also considered a good tactic to use a retainer Fragment to avoid recreating data at every configuration change.
But I don't see how these two can be combined?
Let's say I use a fragment to load a bunch of bitmaps...At OnCreate I check if that Fragment is null or not to get it's data or to instantiate a new one to create them. If i recycle all my bitmaps at OnStop() then there will be nothing left retrieve at the configuration change cause all data will have been recycled.
So....I don't see any way to combine these two tactics. Am I wrong? And if not which of the two is best to use?
My case is about loading images from SD card folder. could be only one pic, could be 500...
and showing pictues isn't all my app does so after this activity there could a need for memory by some other activity.
From Managing Bitmap Memory:
On Android 2.3.3 (API level 10) and lower, using recycle() is
recommended. If you're displaying large amounts of bitmap data in your
app, you're likely to run into OutOfMemoryError errors. The recycle()
method allows an app to reclaim memory as soon as possible.
According to this you don't even need to call recycle on devices running API 11 or higher, so it may not really be a problem for you.
You also really don't need to recycle bitmaps if the app is being destroyed as the system is going to reclaim all memory the app is taking up to begin with.
Recycle is only needed if you are showing a massive amount of bitmaps or large bitmaps and need the memory reclaimed in your app while it's still running.
Another thing to note is with the strategy you're trying, you wouldn't clean resources in the Activity's onStop() but rather the retained Fragment's onDestroy(). OnDestroy() on a retained fragment won't be called on configuration change because the Fragment isn't ever being destroyed. Thus, your resources can stay in memory beyond your Activity's lifecycle and will be destroyed at the end of your Application's lifecycle.
Should I let go of views and other data i'm holding on to in onStop() or onDestroy()?
If I release my application's data in onDestroy() it won't be very memory-friendly towards android, am I correct? Since i'm still holding on to a couple of views after onStop(). Plus it's not guaranteed to be called and my Activity is purged from memory anyway.
If I release it in onStop(), I have to add do my setContentView() etc. in onStart() which doesn't get the Bundle that onCreate(Bundle) is handed.
Note that I have a very large application which consists of dozens of Views, most of which are custom and added by code rather than layout-files. This is largely due to the fact that I had to create a custom pager to flip through pages, since none of the built-in Views could serve my purposes (I've tried… hard…).
I have read through all the relevant Android docs but I still have no real clue on what about the view-hierarchy Android saves/recreates by itself and what I have to do myself. Or when all of that happens, meaning when Android removes the view-hierarchy from memory.
Update Question:
The android docs says this: Note: Because the system retains your Activity instance in system memory when it is stopped, it's possible that you don't need to implement the onStop() and onRestart() (or even onStart() methods at all.
If it's ok to hold on to everything, why should I care about memory-leaks when my application is being stopped like this article says? If it's destroyed and re-created, for example after a screen-rotation, I'm starting from scratch anyway?
No, you do not have to let go of anything in onStop() or onDestroy() if you only hold it in your activity (in its non-static fields). When Android let's go of your activity, the rest of your stuff is automatically thrown away (along with the activity), because there is no way to reach it from any thread (this is how Garbage Collectors work, it is not in any way special or specific to activities).
The article you refer to describes the problem where a reference to a view (or a drawable, or - broadly speaking - activity context) survives the activity that created it. Since there is a reference pointing back to the already dead activity, it becomes a zombie; what's more, it clings to all its fields, effectively zombifying them too. So if you have a view or a drawable and put it in a static field (or in any other place that might survive your activity object), then yes, you have to let it go in onStop() or onDestroy() of the relevant activity.
If Android destroys your Activity and forgoes calling onDestroy(), it means that the whole process was taken down, and this means that no memory leak can occur anyway (they are local to a single process)
Bonus answers:
views inflated from XML files take exactly as much memory as ones built in code. Why should they be heavier?
(update, after a comment) before an activity gets thrown away, Android walks its whole view hierarchy and gives each view a chance to store its state (any parcelable data) into a bundle; when recreating view Android walks the tree again and gives the data back. This is why when you recreate an activity the state of the view is saved (focus, position, content of text fields etc.). Observe how the state is only saved for elements that have an id (does not matter if they are inflated or created dynamically).
Is it a good practice to null references to Activity Context, when my Activity finishes? I have 3 AsyncTask's, each of them can be running in several instances simultaneously. The update the UI in onPostExecute(). Nulling all Activity Context references in onDestroy() would be quite difficult and make the code messy. What is the best thing to do?
Check WeakAsyncTask for an example from Google of an asynctask that doesn't keep references alive beyond the activity lifecycle, and BetterAsyncTask from DroidFu for an example of a way to wire AsyncTasks so they can reconnect to new activity instances (after a rotation, for example); usage example is here.
There's probably not too much harm in keeping the references to Activity around for a short operation (e.g. a single small web request or small file write), but if there's the possibility for the tasks to pile up, it could cause a problem. For example, if your app reads a 200KB XML file from a server on creation, which let's say can takes 1 minute or more over EDGE, a quick flip of the phone open/closed 3 or 4 times could lead to 4 retained Activity instances -- you can run out of memory pretty quickly in this situation, not to mention the duplicated work.
For any really long-running processes, though, you should definitely consider an IntentService instead of an AsyncTask. They're designed for longer-running processes that aren't really tied to a specific activity - like how you can send an MMS and leave the activity to go do other things, and you get a nice happy toast informing you of the completion of the task whenever it finishes.
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself). Try using the context-application instead of a context-activity. Nulling the references is not needed when you do not need the opposite because of the memory troubles.
If the tasks will be eligible for garbage collection themselves shortly after your Activity finishes, I see no problem in keeping the references around.
If the tasks do outlive the activity for a significant amount of time, you should set all references to the Activity Context to null. See also the article Avoiding Memory Leaks.
Either way, it is good practice to use the Application Context (getApplicationContext()) instead of the Activity Context whenever you can. In this case you can't do this, because you need to post UI messages; I'm just mentioning it for completeness.
You can hold context reference using Weak reference. http://developer.android.com/reference/java/lang/ref/WeakReference.html
Weak referenced objects can be GC'd. You should just do check if you context reference is still valid every time you use it.