public class TestActivity extends Activity {
public static TestActivity mTestActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTestActivity = this;
}
#Override
protected void onDestroy() {
mTestActivity = null;
super.onDestroy();
}
}
Can I ask a very rookie question?
Does static link to Activity ALWAYS leads to memory leak? Even if I 'null' it on destroy?
EDIT:
If there is a memory leak even though you 'null' it on destroy, could you explain why?
I have some years of Java experience but I cannot understand why 'null' on destroy does not free the memory.
If, you null it on destroy there is not point in keeping it static. As for the leak, yes, I think you will (if you change activities). It would be easier to just keep a (non-static) reference to the application.
onDestroy runs once - if ever. Till then you have your leak. Unregister in onPause(). Notice that pre-Honeycomb onPause() might be the last method that would run - not that it makes a difference in your case as a process being killed takes its classes with it (and their leaks). Or so I think. I don't know if an activity can be killed without its onDestroy being called (not in the case the whole process goes down - then it is perfectly possible but it also makes no difference as I said) - I don't think so though.
In short your leak exists as long as your activity is alive (instantiated)
Related
I have an Android application with a single Activity (MainActivity). I also have a static state variable (foo) that needs to be started and stopped with MainActivity. The lifetime of foo must match the entire lifetime of MainActivity, not its visible lifetime, nor its foreground lifetime. Here's the basic gist:
static Foo foo;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
foo.start();
}
protected void onPause()
{
super.onPause();
if (isFinishing())
disposeFoo();
}
protected void onDestroy()
{
super.onDestroy();
disposeFoo();
}
private void disposeFoo()
{
if (foo.isRunning())
foo.stop();
}
Every now and again I will get a crash report that says: Foo has been started while already running.
I can start and stop MainActivity all day long from the app launcher and this crash won't occur. As far as I know, no one is calling startActivity on MainActivity either.
Is it expected behavior that a new instance of MainActivity will be created, and onCreate called on it before the onDestroy is run on the old instance all within the same application? In what circumstances would this occur? Is there a different pattern I should be using to initialize libraries, databases, and other singleton objects?
Your app is in very often killed if some other application with higher priority (generally, if it's in the foreground it's higher priority) needs the resources. This is due to the nature of mobile devices having relatively limited resources. You will find that it's static variables may be null once you return to it, so static variables for a longer periods of time in Android is a bad idea.
You should save your data somewhere more durable. You might find this article on general Data Storage to be useful. This question should be relevant too: Saving Android Activity state using Save Instance State
Because onDestroy is not called if, for example, activity was recreated because of screen rotation.
From the Activity documentation:
The final call you receive before your activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
So you should call foo.start/foo.stop in onStart/onStop or onResume/onPause.
--
Update:
If I understand correctly, the problem is that you are tied to a singleton/monostate object Foo the should be unique for all objects, and must be destroyed when ALL activities are destroyed.
The problem is that nothing can guarantee that only one instance of activity has a runinng Foo, because onDestroy can be called after a new instance is created.
So the solution is to use an Instance counter:
public class Foo {
private int instCounter = 0;
public synchronized void start() {
...
++instCounter;
}
public synchronized void dispose() {
--instCounter;
if (instCounter == 0) {
// dispose
}
}
This should do the trick.
I have an application works well in emulator and mobile but if we close the application by clicking on exit button of the phone(not from application).and after few hours we are reopening the application, it gets opened from middle of the application(not from the first screen).and after using that app some times it gets hanged and message is displayed 'unfortunately app has stopped '. Is this mobile problem or application problem.
I suggest reading the Activity documentation.
The Android OS has its own application lifecycle management.
Each activity is kept "alive" until its onDestroy is called. For example, the OS can keep an activity alive for several hours and then kill it when there is not enough memory to perform other tasks.
What happens in you case is most likely that the same activity re-runs when you open you app again (in the emulator the activity is probably killed before) and you're in a bad state since probably some of the objects were disposed or re-initialized.
The right thing to do is use some of the other state callbacks, such as onPause/Resume to allocate/dispose resources used by the activity.
You code might look like this:
public class SomeActivity extends Activity
{
public void onCreate()
{
super.onCreate();
// Do some object initialization
// You might assume that this code is called each time the activity runs.
// THIS CODE WILL RUN ONLY ONCE UNTIL onDestroy is called.
// The thing is that you don't know when onDestry is called even if you close the.
// Use this method to initialize layouts, static objects, singletons, etc'.
}
public void onDestroy()
{
super.onDestroy();
// This code will be called when the activity is killed.
// When will it be killed? you don't really know in most cases so the best thing to do
// is to assume you don't know when it be killed.
}
}
Your code should look something like this:
public class SomeActivity extends Activity
{
public void onCreate()
{
super.onCreate();
// Initialize layouts
// Initialize static stuff which you want to do only one time
}
public void onDestroy()
{
// Release stuff you initialized in the onCreate
}
public void onResume()
{
// This method is called each time your activity is about to be shown to the user,
// either since you moved back from another another activity or since your app was re-
// opened.
}
public void onPause()
{
// This method is called each time your activity is about to loss focus.
// either since you moved to another activity or since the entire app goes to the
// background.
}
}
bottom line: always assume the same activity can re-run again.
Actually, that particular application is not closed properly. It is application error only.
I have an activity that looks like the following:
class MyActivity extends Activity {
Runnable refreshTimer = new Runnable() {
public void run() {
refresh();
}
};
protected onCreate(...) {
handler.postAtTime(refreshTimer, ...);
}
protected onDestroy() {
handler.removeCallbacks(refreshTimer);
}
protected void refresh() { ... }
}
After onDestroy is called, there are still messages in the activity's MessageQueue that contain references to MyActivity$0 (the refresh Runnable) for some reason. Because MyActivity$0 has an implicit reference to MyActivity, this causes a memory leak of the MyActivity context.
The result of merge_shortest_paths for android.app.Activity excluding phantom,soft,weak,etc references using Eclipse Memory Analyzer Tool:
(The source code above is a simplification of the actual object relationship displayed in the MAT dump)
Shouldn't calling removeCallbacks remove any references to the runnable objects from the Queue? Why am I leaking contexts?
Something to try:
According to the android docs:
OnDestroy:
The final call you receive before your activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
When you are exiting your activity, it looks like there are still a bunch of queued up messages and the context for unregistering is not invoking the cancel callback.
What you should do is unregister your runnable in onPause:
This callback is mostly used for saving any persistent state the activity is editing, to present a "edit in place" model to the user and making sure nothing is lost if there are not enough resources to start the new activity without first killing this one. This is also a good place to do things like stop animations and other things that consume a noticeable amount of CPU in order to make the switch to the next activity as fast as possible, or to close resources that are exclusive access such as the camera.
Typically a Receiver or "Scheduled" Runnable will register in onResume, and unregister in onPause for better lifecycle pairing
Without seeing what you are doing in refresh, it is tough to tell, it could be leaking due to activity references that are activity scoped referenced in the refresh method.
I've been bothered by this "characteristics": When I use Back button to leave my app, I can tell onDestroy() is called, but the next time I run my app, all the static members of the Activity class still retain their values. See the code below:
public class HelloAndroid extends Activity {
private static int mValue; // a static member here
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText((mValue != 0) ?
("Left-over value = " + mValue) : "This is a new instance");
setContentView(tv);
}
public void onDestroy() {
super.onDestroy();
mValue++;
}
}
The above code displays the left-over value in mValue, and it increments when the session ends so that I know for sure the onDestroy() is called.
I found a useful answer on this forum, and I understand in the above code mValue is a class member, instead of an instance member. But isn't it true that, in this particular case, I have only one single HelloAndroid activity, and so When he dies, everything is cleaned up, and the next time I come back, everything starts over again? (Or, is there some other misterious thing in the system still holds on to it after onDestroy() so that it just won't die???)
(The above is just a variable, what if it's a bunch of objec references? Each piece is a separately re-collectable memory. Is there a chance that GC collects some of them but not all-or-none? This really bugs me.)
The OS decides when things "go away." The onDestroy is there to let your app have a final chance to clean things up before the activity does get destroyed but it does not mean that the activity will, in fact, be GCed. Here is a good article that I recommend people to read that relates to creating an exit button. While it's not exactly what you asked about, the concepts will help you understand what's going on.
You don't just have the Activity though. You also have the application, and its process running in a Dalvik VM. Android will usually leave the application running in the background until it needs to reclaim the memory it is using for some other application. Your static member should remain in memory as long as the process is running. If you try running some memory-intensive application or forcefully closing the running application with some task manager, you may see the static value reset.
I am having SearchCritiera object and i make it singleton and declare this variable as static,
now problem is if i left my application remain open for couple of hours that static object is removed by Android OS, how can i make sure static object should not be removed by the OS.
like i know there are few keywords like
Weekreference and softreference is there any strongreference keyword which can tell Android OS do not remove the reference ??
Don't use static references, even if your application remains in the foreground, these objects may be destroyed by the garbage collector (I've seen this happening a couple times now).
You can simply avoid this by storing them in your unique Application instance. That object is guaranteed to live as long as your app.
Sadly, you can't force Android to keep your application in memory. If the OS feels it needs more memory for a foreground application or service it reserves the right to terminate one or all of your Activities.
I'm assuming what's happening is your static object is being lost and re-created when you call it, meaning that it has lost its state. That said, if you have a reference to the object in a foreground Activity I'm a little surprised that it's getting lost.
The best you can do work around this is hook into the lifecycle events and save the state of your singleton object and then restore it when appropriate.
Unfortunately, there are no Application wide lifecycle events in Androd. Have a look at this question for how to save transient application state.
If i am not wrong when application remain open for long time data is released by the android OS, and while stopiong activity it will call "onSaveInstanceState" and when can i store searchritiera into this method and will it retrive back when "onRestoreInstanceState" is get called ?
private static SearchCriteria searchCriteria;
#Override
public void onSaveInstanceState(Bundle outState)
{
outState.putSerializable(WLConstants.SEARCH_CRITERIA, searchCriteria);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if(savedInstanceState != null){
searchCriteria = (SearchCriteria)
savedInstanceState.getSerializable(WLConstants.SEARCH_CRITERIA);
}
super.onRestoreInstanceState(savedInstanceState);
}