I am currently building an android application that will be run only on tablets.
One feature is displaying a list of events in a (custom created) dialog.
In some cases, when the user acknowledges these events, the dialog is not beeing garbage collected, and after opening the dialog several times, the number of instances of the dialog that are kept in memory is increasing and increasing.
Since the app is to be run for a long time I guess this will lead to memory issues.
So I took a memory dump and fired up the Eclipse Memory Analyzer (MAT).
I never had this kind of problem before and I am not too familiar with MAT but here is what I assume:
I can see that there are several instances of my Dialog and some "inner class" (MyDialog$1) still there. For all other "inner classes" (MyDialog$2, MyDialog$3, ...) the count is 0.
Using "Merge shortest paths to GC Roots" with the option "with all references" leads me to
android.view.ViewRoot$RunQueue$HandlerAction, so my assumption is that somehow there is a reference of one of my listeners kept?
I hope you can tell me if my assumption (and my way of analyzing) is correct.
And hopefully you can give me a solution or hint on how to solve this.
Thanks in advance
Sven
I think that you are correct in your assumption. A listener that is an anonymous class will have a reference to the enclosing class. You should unregister the listeners when you are want the dialog destroyed.
Alternatively, you might consider using an API to handle the lifecycle of the Dialog. The deprecated Activity.showDialog and Activity.removeDialog should remove all references to the dialog when it is removed.
In Android 3.0 and above you can use the DialogFragment instead of Dialog, and the FragmentManager should handle it's lifecycle for you. See the Fragments developer guide.
Related
I have an app where I use Butterknife, and recently I found a fragment where I had failed to call unbinder.unbind() in the fragment's onDestroyView(). I fixed the problem but it made me start thinking.
What kind of errors can this cause and why? I don't have a particular error right now but I would like to know what to watch out for in the future, and the website for the library doesn't specify the problems this could cause.
Imagine you have a retained fragment and you have initialized a view using #BindView.
An orientation change happens, which results in destroying activity instance, but not this fragment, because this fragment is a retained fragment, which means, that the field that you have initialized is still there (not null) and is holding a strong reference to the view of the previous activity, which results in leaking of the activity.
Although this might take for some small amount of time (because eventually you are going to perform another ButterKnife.bind() in onViewCreated(), right? But who knows, maybe you won't), still it is better to release resources as soon as you do not need them and let the GC do its job.
I've also thought about this question some time ago and other than this I couldn't come up to another scenario where unbind() would be strongly necessary.
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.
Code is probably too complex to post here in full, but here is the basic schema: I have two Activity subclasses, each of which hosts a ListView. Each ListView has an adapter of a custom class, which generates View instances also of a custom class. These lists are showing data items that are generated asynchronously in another thread; as it needs to know where to send updates to, the data objects it manipulates have WeakReference<> objects that are set to hold references to the adapters displaying their contents when they are initialised. When an object in the list of the first activity is selected, I start the second activity with an intent that instructs it to look up the item and display its contents. I then use the 'back' button to close the second activity and return to the first. For some reason when I run this with StrictMode checking enabled, it always crashes after a few iterations of switching between the two activities, complaining that there are too many instances of one of my Activity classes.
I have arranged for a heap dump to be written just prior to the crash (see Android StrictMode and heap dumps). These heap dumps always show that there is 1 instance of each of my two activities on the heap at the time of termination. First of all, is this not to be expected when I have recently switched between the two, and if that is so, why is StrictMode complaining about this? If it isn't expected, how can I arrange to avoid this? Examining the heap dump, both objects are referenced from the main thread stack, over which I don't seem to have any useful degree of control. Each also has a reference from android.app.ActivityThread$ActivityClientRecord, which I also do not seem to be able to control.
So, basically, any ideas how I avoid this situation? Does this actually represent an activity leak, or is StrictMode just being overly sensitive?
I know that this is old post. Just for guys who is looking for solution and explanation to this problem.
In case there is InstanceCountViolation exception it means that there is a real problem that Activity leak. Otherwise there can problem which is related to how detectActivityLeaks check is implemented in Android SDK.
To identify if this is a problem I can recommend the following post: Detecting leaked Activities in Android. If you will see that there are objects holding a reference to this activity which don't related to Android Framework then you have a problem which should be fixed by you.
In case there are no objects holding a reference to this activity which don't related to Android Framework than it means that you encountered with the problem related to how detectActivityLeaks check is implemented. In this case to fix the problem with failed activity without turning off detectActivityLeaks you can simply run System.gc() before starting activity in debug configuration like in the following example:
if (BuildConfig.DEBUG)
{
System.gc();
}
Intent intent = new Intent(context, SomeActivity.class);
this.startActivity(intent);
More information are available in this answer.
(Note that I've searched online for the warnings I'm describing below, and have come up with next to nothing about them.)
I'm working with API level 10. I have a preference screen (XML-based), and one of the options in there creates a custom ListActivity as follows:
PreferenceActivity contains an option that creates a...
ListActivity which is a dialog that employs...
setOnClickListener() which contains an onClick() method that (right before calling finish()) will startActivity() a new Intent...
sub-Activity which starts up an...
AsyncTask which does variable time work which when done calls...
onPostExecute() which calls finish()
The thing is, it works... but I'm getting a raft of warning starting with:
10-16 21:59:25.010: WARN/WindowManager(170): Rebuild removed 4 windows but added 3
10-16 21:59:25.010: WARN/WindowManager(170): This window was lost:.....
Curiously, this raft of warnings ONLY comes up when the task executes quickly! When I added a Thread.sleep() call to my AsyncTask to artificially inflate its runtime it worked and threw no warnings whatsoever. In fact, as long as it takes more than (roughly) 500 ms to run it works fine. (Note that I tried using startActivityForResult() to no greater effect - the same problem occurs.)
The goal is that the user selects a preference item, they change its setting, some processing takes place, and then the user is left back at the preference menu they started on.
I'm betting it's a race condition... the order in which the windows are destroyed varies depending on that run-time... and I get the impression that when the sub-Activity closes before its parent ListActivity the warnings get thrown. But sprinkling a 1s sleep() in isn't a reasonable solution unless this is some sort of Android bug (unlikely, but then again I've reproduced a couple of those today already).
So, what's the flaw in this my that leads to this stream of warnings? It'd be nice to say "on preference, do this, then do that, then finish" but I think what I'm doing is the equivalent. Maybe not... thoughts?
Edit: I decided to try doing this ListActivity as a custom Dialog... that was one of the more painful things I've tried to do lately (getApplication() doesn't work and lots of other things seem to go wrong... it may be inexperience to some extent, but dialogs really weren't meant for this either...
Try the following two things:
Dismiss your dialog before calling finish() on its parent activity (PreferenceActivity).
Make sure you are starting your AsyncTask later in the sub-activity's lifecycle. I'm specifically thinking you should launch it in onResume().
My best guess is that the AsyncTask is calling finish() on the sub-activity, before the sub-activity has had a chance to fully start up. Why that would matter? I'm not sure. Something to try though. Good luck!
I have activity A, which launches activity B via an intent. Activity A has no references to Activity B, there are also no references to Activity B in the Application singleton I am using.
When I create Activity B, several thousand objects are created. That's okay, it's an activity with a very populated ListView with lots of images.
But, when I press the back button and return to Activity A, only about a dozen of the several thousand objects are released. onDestroy() is also called for the activity. I'm using DDMS to view the heap info, and am pressing 'Cause GC' several times to force it to free memory.
I've done the same test on other apps (that use list views too) and 100% of their objects are destroyed on pressing the back button then 'Cause GC', so it's certainly not a bug.
Any advice please? :-) I've read the material in the android docs about leaking contexts, but that's not helpful since i'm not referencing the activity (or anything in it) being destroyed elsewhere. Also, I have many other activities which work the same way, and don't release all memory on destroy. I must be missing something obvious?
Edit: I just realized i'm using AsyncTasks which have references to the activity (either passed as arg into doInBackground() or accessible via outerClass.this. Could they be hanging around in the thread pool, even after onPostExecute() ?
Edit: It leaks even if I go back before running any asynctasks :-(
Edit: No leak before running asynctasks if I remove admob code, but still leaks in the activites which do use asynctasks.. so asynctask is still a good candidate :-)
I believe there's a bug in the ListView implementation. Take a look at this question: Android: AlertDialog causes a memory leak.
Here's a bug report: http://code.google.com/p/android/issues/detail?id=12334. It's declined but in my opinion it must be reopened.