Can we rely on onStop() being called? - android

Concerning Android's Activity lifecycle, I've seen it widely claimed (on SO and elsewhere) that persistent data should be saved in the onPause() method, because there is no guarantee that onStop() will be called, since the OS may need to kill the activity to reclaim its resources if system resources are running low.
However, in a book I'm reading, the opposite is stated:
Practically speaking, the OS will not reclaim a visible (paused or
resumed) > activity. Activities are not marked as "killable" until
onStop() is called and finishes executing.
[Talks about stashed
state and activity record a bit]
Note that your activity can pass into the stashed state without
onDestroy() being called. You can rely on onStop() and
onSaveInstanceState(Bundle) being called (unless something has gone
horribly wrong on the device) ... Override onStop() to save any
permanent data, such as things the user is editing, because your
activity may be killed at any time after this method returns.
p70-71, Android Programming: The Big Nerd Ranch Guide, 3rd Edition
(emphasis mine)
Multi-Part Question:
Is this (possibility of an app being killed before onStop() is
called) something that is no longer true in and is still being
propagated, or is the book outright wrong?
Is there some nuance to when or why an Activity might be killed that
makes the answer "sometimes?"
If the book is correct, why is that misconception so widely spread? (E.g. Here, here, and here.)
References to official documentation would be appreciated.

This is an illustration of Activity lifecycle :
According to the official Android documentation,
onPause() execution is very brief, and does not necessarily afford
enough time to perform save operations. For this reason, you should
not use onPause() to save application or user data, make network
calls, or execute database transactions; such work may not complete
before the method completes. Instead, you should perform heavy-load
shutdown operations during onStop(). For more information about
suitable operations to perform during onStop(), see onStop().
There is a case when the app go from onPause() to onCreate() without onStop() and onDestroy() is when another app with higher priority needs memory as you see the illustration.
Also, The activity could be destroyed without calling onStart(),
onStop() when you call finish() method on onCreate() see reference

Related

Android onDestroy and Onstop not guarantied to be called

In my android app (written in kotlin) I would like to do some data saving when my activity is destroyed. But I have a problem. I've done researching and found out that onDestroy() and onStop() methods are not guarantied to be called. So practically my activity can be destroyed without calling onDestroy() method which would be disaster for me.
Please, does anyone have some gentle solution for this problem?
Some additional information:
The purpose of overriding onDestroy is if you need to clean up leakable resources, like something that uses native memory or a spawned Thread that is holding onto object references. It will never "become safe" like you suggested in the comments because using it as a hook for saving state is not its purpose. The reason it is not guaranteed to be called is that if the OS is shutting down your application completely, the entire heap of memory for your app is released, in which case it would be redundant to call it.
The majority of apps should never need to override it because most apps don't allocate memory directly, and there are better techniques for handling asynchronous work than spawning Threads directly.
You are right about onDestroy()
do not count on this method being called as a place for saving data!
For saving state you should use onPause(), onSaveInstanceState() or
onStop() - which gets called as soon your Activity is no longer visible to the user.

Android contradicting documentation about lifecycle callbacks onPause() and onStop()

Intuitively, the most applicable callback to perform stuff in it would be onPause(). Yet, there seems to be a contradiction in the documentation:
According to https://developer.android.com/guide/components/activities/activity-lifecycle.html:
onPause() execution is very brief, and does not necessarily afford
enough time to perform save operations. For this reason, you should
not use onPause() to save application or user data, make network
calls, or execute database transactions; such work may not complete
before the method completes. Instead, you should perform heavy-load
shutdown operations during onStop().
You should also use onStop() to perform relatively CPU-intensive
shutdown operations. For example, if you can't find a more opportune
time to save information to a database, you might do so during
onStop().
According to https://developer.android.com/reference/android/app/Activity.html:
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.
So, where should it be done and on another spawned thread? How is this commonly done?
Good catch!
Some notes to sum up:
don't use onPause() for "really heavy-load shutdown operations", otherwise you risk to get a negative user experience by slowing down a "next activity to be resumed until this method returns." In other cases go with it;
if you still hesitate, use onSaveInstanceState(). It's called between onPause() and onStop() and, AFAIK, is guaranteed to be called. The system relies on the method "so that when an activity has been killed, the system can restore its state coming back to it in the future";
regarding onStop(), personally I have never! experienced killing my app's process until the method returns, but seen SO questions stating so, which, as we know, corresponds to the official documentation. But, it's really very rare. So it's up to you whether to rely on it or not.

Contradiction in Activity lifecycle diagram versus description of lifecycle

I am not clear on what the situation is when an activity is "destroyed" by the OS.
Let me explain why - in the diagram of the activity lifecycle here: http://developer.android.com/reference/android/app/Activity.html
There is an arrow going directly from onStop() to 'App process killed' then an arrow from 'App process killed' to OnCreate().
This diagram therefore shows that onDestroy() is NOT called if the OS kills the activity due to memory constraints etc.
However in the description of the lifecycle the word "destroy" is used many times. For example the following quote from this page: http://developer.android.com/training/basics/activity-lifecycle/recreating.html
The system may also destroy your activity if it's currently stopped
and hasn't been used in a long time or the foreground activity
requires more resources so the system must shut down background
processes to recover memory.
So the documentation is saying the activity is destroyed yet in the diagram the arrow goes from onStop() to onCreate() and bypasses onDestroy(). This is why I am confused as it is apparently a contradiction.
If I have an activity which creates some objects in its onCreate() method and I set them to null in onDestroy() but onDestroy() is not called if the app moves from onStop() to onCreate() then won't I have a memory leak as they will get created again in onCreate()?
I can't set them to null in onStop() because then if the lifecycle moves from onStop() to onRestart() to onStart() they will be null.
Therefore how does one deal with the correct sequence of creating and destroying of child objects within an activity in order to deal with all paths in the lifecycle diagram?
Is it necessary within onCreate() to only create the objects if they are null and not otherwise?
This diagram therefore shows that onDestroy() is NOT called if the OS kills the activity due to memory constraints etc.
The arrow in question is labeled "Apps with higher priority need memory". Hence, this diagram shows that onDestroy() is not called if the OS terminates the process because apps with higher priority need memory. Android will terminate the app's process more gently in other cases, and in those cases, Android will take the time to call onDestroy() on your activities. onDestroy() is also called in other scenarios, such as finish(), the default behavior of the BACK button, the default behavior on a configuration change, etc.
If I have an activity which creates some objects in its onCreate() method and I set them to null in onDestroy() but onDestroy() is not called if the app moves from onStop() to onCreate() then won't I have a memory leak as they will get created again in onCreate()?
No, because the whole process is terminated "if the app moves from onStop() to onCreate()". Android does not destroy individual activities due to low memory conditions, despite some statements in the documentation to the contrary.
Therefore how does one deal with the correct sequence of creating and destroying of child objects within an activity in order to deal with all paths in the lifecycle diagram?
Lots of things should be cleaned up in or before onStop(). Specifically, anything that you're doing that may cause the user to regret having installed your app, such as requesting GPS fixes, should be considered for cleanup in onPause() or onStop().
For those things that you determine properly should be cleaned up in onDestroy(), do so. AFAIK, there are only three possibilities:
You are called with onDestroy() and can do your cleanup work
Your process is terminated, in which case your cleanup work is no longer needed or possible
You crashed with an unhandled exception, in which case Android is not guaranteed to call any more lifecycle methods (moral of this story: use good exception handlers)

How can we guarantee that onPause will be called?

Going of this question
android save game state in onPause or onDestroy?
The answer highlighted that the person asking the question should save game state in onPause because the onDestroy documentation says "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." In these situations, how can we ensure onPause will be called? Is it possible to control what methods are called in these situations? Seems unpredictable
onPause will be called every time your activity leaves the screen, before it is finished or if something overlays your activity that keeps the user from interacting with your activity.
You don't need to worry that onPause is not called.
For more information look into the starting and stopping activity training or the Lifecycle Api Docs
You should save temporary instance state in onSaveInstanceState(). This method recieves a Bundle parameter which you can use to write state to. The Bundle is then sent to onCreate() when your app is restarted.
If you need to save instance state more permanently, then save it to a database or SharedPreferences. According to http://developer.android.com/training/basics/activity-lifecycle/stopping.html, you should use onStop() for more CPU-intensive operations rather than onPause(). I don't think you need to worry about onPause() being called in normal use cases. As far as I can tell, it is guaranteed. (Of course, there are always catastrophic failures, such as the user pulling out the battery of the device, but there's not much you can do about that.)
It's almost certain that onPause() will always be called. The reasoning is that it's highly unlikely the "system" will aggressively kill an active (i.e., visible) Activity.
In order to free resources, the Android OS effectively reserves the right to kill dormant processes - in particular ones that have been long-running but not recently used. In order for your Activity (and its process) to qualify for this, at some point you must have sent the Activity to a background state, e.g., pressed HOME or started another app's Activity. The simple fact your Activity is no longer visible means it will have been paused and onPause() will have been called. It's almost certain that the Activity will have been stopped and onStop() will have also been called.
What you have to remember is Android has been around for a while now and early devices had RAM measured in 100s of MB and the system behaviour possibilities and documentation reflects this. More recent devices have RAM measured in GB (even 10s of GB) so unless a user really pushes their device to the limitations of its resources, aggressive clean-up of app processes becomes less likely.
You can show Dialog for User to Act Accordingly.When Dialog will be opened to ask user that he wants to quit or not,Activity will definitely call onPause() and at this time you can write your game state saving code. Its better to write code on onPause() because at onDestroy() resources are freed by os.

android save game state in onPause or onDestroy?

I am trying to implement a 'resume' feature for a game I'm developing. It should work as follows:
If the user starts a game and later closes the game with finishing, the game state is saved. When opening the app again, a 'resume' option will be available.
If the activity is only paused (e.g. minimized due to a phone call) and the user returns, it should show the game in progress. It should not terminate and save the state, unless of course, the OS decides to kill the activity.
I've decided to use SharedPreferences for the most part, as well as a custom file to save extra information. I've seen a lot of people recommend saving the state of the program in the onPause() method and I've been wondering why this is the case.
From what I can gather, using OnDestroy() would be better. onPause() does not mean the activity will be killed so I could be wasting time when saving the game state necessarily. I've checked my program and onDestroy() is being called at the appropriate times. I'm assuming therefore I have no leak keeping the activity from being destroyed.
Despite this, I cannot find anywhere recommending to save state in onDestroy() and everyone seems to recommend using onPause. Am I missing a piece of information here?
Despite this, I cannot find anywhere recommending to save state in onDestroy() and everyone seems to recommend using onPause. Am I missing a piece of information here?
You are :). onDestroy() is not guaranteed to be called. See the documentation for it:
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.

Categories

Resources