I've been staring at my app's log file for a couple of hours and just want to make sure what I see is even possible.
I have an Activity called ActivityA.
It starts up ThreadB.
ThreadB communicates with a server and sends the results back to ActivityA.
I am logging messages in ThreadB when it receives results from the server and sends them back to ActivityA
I am logging messages at the start and end of ActivityA's onDestroy() method.
Here's the weirdness.
ActivityA's onDestroy() method completes. I know it completes because I see the message I print at the end of it in the log.
Then, ThreadB gets some very late input from the server. ThreadB delivers it to ActivityA.
The routine in ActivityA that ThreadB communicates executes. I know it executes because the messages it displays as it runs show in the log file AFTER ActivityA.onDestroy() has completed.
I thought once onDestroy() completed ActivityA would be unreachable. IOW ThreadB could send messages to ActivityA as much as it wanted but ActivityA would not receive these messages because it didn't exist any more.
What in the world am I missing?
You are mixing the Object lifecycle and Activity lifecycle.
If you hold a reference to an Object (the activity in this case) you can execute methods on it.
The Activity lifecycle controls things on a higher abstraction level, determining if it's executing, being presented to the user, etc. It does not control whether methods can be invoked on the Activity object.
You can see this with a simple example:
public class DummyActivity extends Activity {
public int nothing() {
return 1;
}
}
DummyActivity act = new DummyActivity();
act.nothing();
Here I create an Activity and call a method. It does not matter what's the state of the activity, it's only relevant if the object was created and I hold a reference to it.
This might be happening because the ThreadB is a non static inner class of ActivityA, so it has reference to ActivityA, that is why ActivityA is not getting garbage collected. Even if its onDestroy() method is called.
JVM keeps the reference to the currently running thread, and ThreadB in turn has reference to ActivityA. So method of ActivityA gets executed even after onDestroy().
Make the tread as static inner class in your Activity and do not forget to stop your thread in onDestoy(). This would stop the method of ActivityA get executed after onDestroy().
Callback onDestroy does not destroy object Activity, it's still safe to make calls to methods inside some Activity, Fragment or whatever after onDestroy called if you do not touch view elements.
By the way expected behaviour can be reached via Intents and BroadcastListener.
Just implement BroadcastListener with action "my.log.intent" in your activity, register it onStart() and unregister onStop().
Then send broadcast from your logger:
Intent intent = new Intent("my.log.intent");
intent.setExtras(someUsefulData);
LocalBroadcastManager.getInstance().sendBroadcast(intent);
and there you go:
your someUsefulData will be available to Activity regarding activity lifecycle. I.e. when activity visible to user and you are able manipulate contained views.
Related
I looked in the Android source code and neither Activity nor any of its ancestor classes ContextThemeWrapper, ContextWrapper or Context override the equals method. Can equals be safely used to compare Activity objects?
Reason I want to do this: I have a notification queue that needs to be managed by the currently active activity. The activity will start managing it in its onStart and stop in its onStop. The problem is that in practice I have found when switching activities the new activity onStart can be called before the old activity onStop, and only one activity can be managing the notification queue at once. So I want to store a variable like "currentActivity" which activities can compare themselves to, to determine if some other activity onStart has already been called and therefore they don't need to take any action onStop.
My ex colleague developed a service which plays music in background. However if user wants to exit from program then application calls below code to stop service. Service stops but i cannot handle it while overriding onDestroy method
Activity realAct = context.getParent();
if (realAct == null) {
realAct = context;
}
ContextWrapper cw = new ContextWrapper(realAct);
cw.stopService(new Intent(cw, MyPlayerService.class));
edit:
Briefly, Activity Main starts Activity Sub then Activity Sub starts Service. Then i press back button so Activity Sub finishes. When i am in Activity Main I call above stopservice code. So onDestroy method of Service is not called
Try again to use method like that in code of your service :
#Override
public void onDestroy()
{
super.onDestroy();
//your code
}
It has to work !
Are you trying to stop the service when the user exits from application?
onDestroy is called when system needs to kill activities due to insufficient resources. Just because you pressed back on your app doesn't mean onDestroy gets called immediately.
But if you want to force close your app to have it automatically invoke onDestroy, call
finish()
on wherever you are exiting your application from.
If you want to invoke onDestroy upon user pressing back button to exit, Override onBackPressed() and invoke finish() from there.
But really, this is an antipattern so I'd recommend using onPause() instead.
Edit Upon OP Edit:
I think we need to have more details on how you are instantiating the service but here are some additional thoughts:
Have you overridden onDestroy properly and calling the super method?
If this is the case, the only other possibility I can think of is that you still have ServiceConnection objects bound to the service with BIND_AUTO_CREATE set. Until all of these bindings are removed, service will not get destroyed.
So make sure to do both:
unbindService(conn);
stopService(intent);
In Activity, I register receiver in onCreate and unregister it onDestroy. It should works fine if every onCreate is followed by onDestroy after the next onCreate. Otherwise, if onCreate is being called more than onDestroy, receiver is registered multiple time and the app mis-behaves.
So my questions are:
Is that ok I register receiver in onCreate and unregister it in onDestroy?
Is that onCreate is always followed by onDestroy before next onCreate?
onDestroy is not guaranteed to be called:
http://developer.android.com/reference/android/app/Activity.html#onDestroy%28%29
"
protected void onDestroy ()
Added in API level 1
Perform any final cleanup before an 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.
Note: do not count on this method being called as a place for saving data! For example, if an activity is editing data in a content provider, those edits should be committed in either onPause() or onSaveInstanceState(Bundle), not here. This method is usually implemented to free resources like threads that are associated with an activity, so that a destroyed activity does not leave such things around while the rest of its application is still running. There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
Derived classes must call through to the super class's implementation of this method. If they do not, an exception will be thrown.
"
You may also want to look at this thread:
Activity OnDestroy never called?
onDestroy is called when the activity is being destroyed. Or removed from the back stack, when ever the user doesn't want it or there is no possible way to get back to it. When your activity wants to receive a broadcast that is fine to do it how you are. If there are no dialogs appearing or notifications or toasts appearing after you receive that should be fine also, if you want to be on the real safe side and only have one activity receiving at a time, and only while the activity is visible move these to onResume and onPause.
You could probably some how unregister when another activity has been brought to the front and re-register after?
I have an Android activity we'll call A which has a button and another activity B. When the user clicks the button in Activity A, I'd like to finish A (let both onStop and onDestroy finish running) and then start up the instance of B. When I put a finish() and startActivity() call in the button click listener, the instance of B starts up before the old instance of A finishes. Can someone help me figure out a way to do what I'm looking for?
What you are looking for is not possible and actually is against Android's activity lifecycle implementation.
Correction
It is possible with android:noHistory="true" tag in your manifest, but for what you are trying to do it seems wrong (read the EDIT)... Messing with the activity stack makes a non intuitive application!
Android OS doesn't let you control when activities will be removed from memory (or killed), and therefore all these fancy "Task killers" are so popular (DONT use them, they only make things worse).
When your activity's onStop() is being called, the activity stops completely, and it just hangs in your memory, but that's fine...
If you want to reset the state of activity A, or close the app when exiting activity B, just create a set of rules in both onResume() and onStop(), you can do everything you wish by creating a set of rules in those functions.
for example: have a boolean in activity A that turns true just before calling activity B,call finish() on your activity A's if this boolean is true
I suggest that you take a look at Android's Activity lifecycle diagram, and make sure that everything you do follows the best practice.
EDIT
I saw your comment, it seems like you are trying to create things that are already in your memory, don't recreate them, it's a waste of CPU time, memory, and battery.
Instead, create a static class with a singleton that will hold all your shared data !
I believe you're looking for
onPause()
which is what gets called when the activity is sent to the background. You can do whatever cleanup you want in there. onStop should only be called when a user is exiting out of your program (or launching another one)
onPause is a better place to do this cleanup. See the Saving Persistent State section of the Activity doc.
When an activity's onPause() method is called, it should commit to the backing content provider or file any changes the user has made. This ensures that those changes will be seen by any other activity that is about to run. You will probably want to commit your data even more aggressively at key times during your activity's lifecycle: for example before starting a new activity, before finishing your own activity, when the user switches between input fields, etc.
While I'm not definite that your cleanup is for user changes, the bold sentence above implies that onPause will complete before the next Activity is created. Of course that probably implies that you'll have to move some setup to onResume...
Alternatively, you could move all your cleanup code to a method, let's just call it cleanup and then just call it before you start activity B. You'll have to put in appropriate guards for your onDestroy cleanup too of course.
override finish() method.
implement cleanUp() method.
create boolean isClean=false in the activity
in cleanUp() write your clean up code.
call cleanUp() in your finish()
check for isCleaned in finish() or in cleanUp() if its true then ignore the clean
now before you start B , call cleanUp() and set isCleand=true
after you call B , call finish()
Start activity A
from inside A startService(c) and finsh A
from inside the service , start Activity B
According to the android Activity Lifecycle, the only callback guaranteed to be called (if an activity ever leaves the Running state, which is typically expected) is onPause().
So, I must assume that there are scenarios in which it makes sense to implement onStop() and onDestroy() although they are not really guaranteed to be called.
I understand that onStop() should be implemented when it's possible for an activity to return to the Running state via the Stopped state (why would it do that instead of returning directly is a different question).
But the need for onDestroy(), when I can place all cleanup/state-saving into onPause(), is unclear to me.
Can you describe a real-app situation (i.e. not analogy to driving a car etc.) in which it would make sense to implement onDestroy()?
onDestroy will be called if you explicitly call finish(); yourself.
Your main activity calls startActivityForResult on a map activity.
Map activity with a LocationListener, the user clicks the map and selects say a local restaurant.
The activity then , sets up some extras to be sent back to your main activity, it then explicitly call's finish(); on itself and in the onDestroy kills the LocationListener and other variables you had invoked.
Just found this in the 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.
Can you describe a real-app situation
(i.e. not analogy to driving a car
etc.) in which it would make sense to
implement onDestroy()?
When you want to capture a configuration change. It's all in the SDK:
http://developer.android.com/reference/android/app/Activity.html