Can anyone give a short overview of the cases when and how an activity is killed by the runtime? I'd like to know the difference between the paused and stopped state. What could force the system to destroy a paused activity, exactly the same (low memory) reason as if it was stopped?
I think if an activity is paused because of an incoming phone call (which suddenly causes a low memory situation) the system simply prefers to release ressources of stopped activities. But how is that done? When does the system "kindly ask" the activity by calling finish() and when not, and when does onDestroy() still get called?
Most of what you asked is described pretty well by the documentation, but I think I can clarify a couple of things.
I'd like to know the difference
between the paused and stopped state.
Visibility. The two states are distinct because a paused activity may only be partially obscured by another activity such as one that's had the Dialog theme applied. That requires keeping whatever resources are needed to maintain visual state. Stopped activities can jettison those resources which could make the difference between the activity being destroyed or preserved if resources are tight.
I think ... the system simply prefers
to release ressources of stopped
activities. But how is that done?
It has to. Stopped activities are completely invisible, which makes them better candidates for killing than those that are still contributing something to what the user sees. I've never seen Android yank a paused-but-partially-visible activity out from under one that's resumed, but I suppose it could happen under the right circumstances. The system knows each activity's state because it's what's directing them there.
When does the system "kindly ask" the
activity by calling finish() and when
not, and when does onDestroy() still
get called?
The system will do orderly destruction when it can, but the API only guarantees are that an activity will ever see onPause() and onSaveInstanceState().
ETA: The exact reasons why activities are removed from the stack are in the source. You shouldn't depend on those reasons being universal truth, because there may be a future version of Android that makes its decisions differently.
Par for the course, I see! I see some valuable information, mixed with expensive misinformation. No, the online docs do NOT every specify exactly under what circumstances the process is killed. This is deliberate, since it is subject to change without notice. Sure, the most common reason for onDestroy() to be called is that the system is running out of memory, which is less common on newer phones (since they have so much memory). But there is no guarantee that that be the ONLY reason it is called.
But yes, the 'contract' between Android and the developers is that if you follow the rules, implementing the needed lifecycle callbacks when they are needed, then it will work, and you do not NEED to know exactly under what circumstances onStop(), onSaveInstanceState() and onDestroy() are called.
Now unlike Google, I will admit that the wording of the contract is somewhat vague at points. This is because, among other lesser reasons, they use terms that have a standard industry meaning, such as 'foreground', but they use them in slightly altered senses. And the alteration is either never explained or explained only in obscure places. It also doesn't help that the diagram purports to show "the paths an activity may take between states", yet fails to show that onDestroy() can be called at many times, even bypassing the transition from Resumed to Stopped. Yet the text clearly describes that possibility.
This is why, unfortunately, reading the Application Lifecycle section of "Application Fundamentals" is simply not enough. Instead, one must also read the Javadoc for EACH of the callbacks under that for Activity, and also the section of "Application Fundamentals" on Processes.
After that, it is enormously helpful to put Log.d statements in each of the callbacks and watch the logcat output while you cycle your application through the lifecycle. But even then, do not rely on lifecycle events taking place in the order you see in logcat unless you can find justification for it in one of these online docs mentioned above.
Related
I know this question has been asked a million times, I myself though that I already knew the answer and that the correct one was that the only guaranteed call is to onPause(), so you should save your data there.
However, in many places of android documentation they always suggest not doing heavy work (such as writing data in database) in the onPause() method as it will delay the transition between the activities.
According to Android Developer Guide in Table 1
onPause(): This method is typically used to commit unsaved changes to persistent data, stop animations and other things that may be consuming CPU, and so on. It should do whatever it does very quickly, because the next activity will not be resumed until it returns.
Killable: YES
Then according to Android Developer Reference Guide in the similar table.
It says the same thing but:
Killable: Pre-HONEYCOMB
And they add a little note that says:
Be aware that these semantics will change slightly between applications targeting platforms starting with HONEYCOMB vs. those targeting prior platforms. Starting with Honeycomb, an application is not in the killable state until its onStop() has returned. This impacts when onSaveInstanceState(Bundle) may be called (it may be safely called after onPause() and allows and application to safely wait until onStop() to save persistent state.
Killable
Note the "Killable" column in the above table -- for those methods that are marked as being killable, after that method returns the process hosting the activity may killed by the system at any time without another line of its code being executed.
FOR POST-HONEYCOMB (i dont care about earlier versions):
So, is it OK to assume that any Android device (including different ROMS) will ensure a call to onStop on the activity? And this is the best place to make any time consuming storage writing of the App?
Note: This is extremely confusing as most answers here, sites, books, and even online android tests take as a correct answer that you should save it in onPause and NOT in onStop.
When to save data to database, onPause() or onStop()?
Either. They are nearly identical, particularly on Android 3.0+.
If the activity that is taking over the foreground is a typical full-screen activity, so that the earlier activity is no longer visible, onPause() and onStop() will be called in rapid succession.
If the activity that is taking over the foreground is themed to be more like a dialog, where the earlier activity is still visible, onPause() will be called, but not onStop(), until such time as the activity is no longer visible (e.g., user now presses HOME).
Most apps aren't worried about the "themed to be more like a dialog" scenario, in which case onPause() and onStop() are called one right after the next, and you can fork your background thread to save your data in whichever of those makes sense to you.
However, in many places of android documentation they always suggest not doing heavy work (such as writing data in database) in the onPause() method as it will delay the transition between the activities.
The same is true of onStop(), as both of those methods are called on the main application thread.
So, is it OK to assume that any Android device (including different ROMS) will ensure a call to onStop on the activity?
Both onPause() and onStop() will have the same characteristics from the standpoint of process termination. Either both should be called (normal case) or neither will be called (e.g., you crash, the battery pops out the back of the phone).
And this is the best place to make any time consuming storage writing of the App?
Either onPause() or onStop() are fine places to trigger the work, done on a background thread, to persist your data. If you prefer to do that work in onStop(), you are absolutely welcome to do so. Personally, I'm an onPause() kind of guy.
If you want more safety, storage in onPause.
If your data is so big that you have to storage it for several seconds, you may open a background Service (e.g. IntentService) to save.
You can also check the system version in your code and choose when to save. if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.ICE_CREAM_SANDWICH){}
In most situations this rule of when to save will not be changed by some customed os. But of course there can be some other os which certainly changed it. So the most important thing in android development is that u need to know that everything can be different in different phones.
I know this question is prehistoric, but it seems like people like me still happen to land on this question. So I guess it may make sense to add an answer I found in the documentation, which states:
To save persistent data, such as user preferences or data for a database, you should take appropriate opportunities when your activity is in the foreground. If no such opportunity arises, you should save such data during the onStop() method.
source
But wait, there's more:
Note the "Killable" column in the above table -- for those methods that are marked as being killable, after that method returns the process hosting the activity may be killed by the system at any time without another line of its code being executed. Because of this, you should use the onPause() method to write any persistent data (such as user edits) to storage.
source
So wrapping up, it seems like persisting data onPause() is the best thing to do.
I'm trying to execute some code whenever my activity is killed, but not when it's simply moved to the background (so just calling it in onPause() isn't a solution), and I understand onDestory() is not guaranteed to be called. I've been searching all over and haven't found a way to do this. How can I go about tackling this problem? Is it possible?
Nothing. You can never be guarantied to be called when an app ends, because it can always be terminated abnormally- it could crash, the battery could be pulled out, etc. onDestroy is the closest you can come. But you should never write a program that requires cleanup at termination time.
I'm not sure, but I suspect you've got a few issues which you've conflated into one large issue;
As I read this question you have 2 or 3 concerns:
1) You want to garuntee something happens when the app exits.
2) You have state you want to maintain during onPause() but not after onDestroy()
3) You presumably have some content which should not persist between application usages.
Some answers:
1) As Gabe points out - there is no guarantee anything will ever be called. I can pop the back off the device and pull out the battery. Your calls aren't going to happen.
2) You might try onDestroyView() for this case. It won't be called in many circumstances in which onPause will be (for example when an alert dialog is shown), but will be called in others (when you replace a fragment with another fragment for example).
3) This issue makes me think you may want to reconsider your method of storing/saving this information. If you don't want it to exist on the system when you app isn't in use, it is best to never write the data to the filesystem. (Due to 1). Other options are keeping it in memory. You could also use shared preferences/preferences mechanism but this will still exist on the filesystem until your app is removed.
For reasons outside of my control (so, please no "you're doing it wrong" replies), my Android app needs to play extremely nicely, i.e. when a user hits the home button, my app needs to go away and release all of its resources (which are heayy, more than 1GB RAM consumption etc). It seemed calling finish() in onPause() would do the trick, but here's the problem: onPause() and onStop() also get called when I start an activity of my own, e.g. a preference activity, for which I just want to normally return.
So, my problem is: How can I determine the reason for losing the focus? I can think of two options, neither of which are pretty:
Keep state, i.e. the new activity sets some global flag that I can check in the "covered" activity so it doesn't stop when onStop() gets called. Annoying because every new activity of mine would have to do that.
Use the ActivityManager to check the top activity, and if it's one of mine, don't commit suicide. Maybe better, but the documentation heavily discourages use of the ActivityManager for this type of stuff.
Any ideas?
You are welcome to use onUserLeaveHint(). It may cover some scenarios (e.g., HOME), but not all (e.g., incoming phone call).
please no "you're doing it wrong" replies
IMHO, "more than 1GB RAM consumption" is already "doing it wrong" for a Play Store app. Specialized apps (e.g., specific enterprise scenarios, dedicated hardware) might be able to get away with that.
One approach for option 1 that will make it less annoying to implement and maintain would be to use Application.registerActivityLifecycleCallbacks. At least this way you don't have to implement this logic multiple times in multiple classes or unnecessarily force your activities into a single otherwise unrelated base class.
Not that I necessarily endorse this option. Also note you'll need API 14+.
I am very familiar with the Android activity lifecycle, but I feel like I am missing something that should be pretty obvious here.
I have a multi-activity Android app; it uses static variables (in the application class) to keep a handle on the various objects that are used across the different views. The problem occurs when the app is paused (exited) and Android (presumably) cleans up memory to maintain foreground processes. When the user resumes the app, it occasionally (not always) seems to come back and resume in the Activity that the user left, but with the static variables nulled. Even worse, the activity sometimes seems to resume with the static variables still present, but with internal variables in objects nulled.
I eventually implemented a simple sanity check, which checks that the statics are not nulled (and also the most vital inner variables of relevant objects) and returns the app to start if it fails. This did cut down on a lot on the problems, but I still see the occasional issues with this, as it is simply not practical to check everything for every resume().
What I really need is for the app to restart from scratch if the Android OS decides it needs to clean anything non-GC from memory while the app is inactive. I feel there should be a graceful way to do this, but haven't noticed anything in the documentation.
Any thoughts? How do others avoid this problem?
Using the Application class for preserving state can result in unexpected behaviour if Android decides to kill your process completely. Check out this answer to a similar question
i.e. you should probably use some sort of persistence (SharedPreferences or some DB) in your Activity lifecycle callbacks.
Right so here I am obsessing over memory leaks, and quite frankly not understanding what could really lead to one (and yes I've read the usual links such as http://kohlerm.blogspot.co.uk/2009/02/memory-leaks-are-easy-to-find.html).
I've tried to create some on purpose, for example by leaving a PhoneStateListener subclass inside my activity and opened and closed it a bazillion time, can't see anything in DDMS heap or MAT out of the ordinary. Yet on SO I read over and over again that not only it needs to be deregistered onDestroy, but also onPause (PhoneStateListener() isnĀ“t finished)
Question: is there such a list?
Bonus question: is it true that a PhoneStateListener will create leaks unless it's deregistered onPause/onDestroy etc.
UPDATE: I stand corrected. When re-spawning my app over and over again, even in singleinstance mode, the PhoneStateListener(s) it has registered are still alive after onDestroy was called, and start adding up. I'm currently working on an elegant way to kill them, and will post my results here.
UPDATE2: The correct way to deregister the listener is:
instanceOfTelephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
... according to the API
UPDATE3: As promised linking this to a better phrased question: https://stackoverflow.com/a/4607435/821423
It is good practice for activity to clean up after yourself and prepare to die in onPause() - this is always called before it goes out of focus, and can not interact with user. onDestroy() is possibly called after this (but not guaranted).
Is your activity is not in focus for user, it does not need any listeners anymore, as it can not show results of those listeners.