Release resources in onPause instead of onDestroy
Asked Answered
W

2

15

This is about POST-honeycomb (i.e., Android 3.0+) and the quotes below come from https://developer.android.com/reference/android/app/Activity.html

According to the lifecycle, onStop and onDestroy are killable, this means:

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

  1. In other words, onStop (along the others that occur before this event) is guaranteed to be called, yet at the moment the method returns, the process might die, thus onDestroy is not guaranteed to be called.

    Another quote states:

    For those methods that are not marked as being killable, the activity's process will not be killed by the system starting from the time the method is called and continuing after it returns.

    Followed by

    Thus an activity is in the killable state, for example, between after onPause() to the start of onResume().

  2. But this does not correspond to what was said above, unless this only corresponds to PRE-honeycomb. This is not true for POST-honeycomb, right? So basically, both onPause and onStop are guaranteed to be called.

  3. Assuming I only release a resource in onDestroy, then this might lead to a possible leak since onDestroy might not be called, right?

  4. However, can this scenario (i.e., onDestroy is not called) occur besides when the process is killed by android itself? Are there any other scenarios that cause onDestroy not to be called, thus leaking the resource.

  5. Is it true that when Android kills the process that the resources will be destroyed and no leak can occur (even when we did not explicitly released the resource?).

Please provide detailed information whether those statements (1) (2) (3) (4) (5) are correct or not.

Wolfhound answered 14/4, 2017 at 13:33 Comment(1)
"So basically, both onPause and onStop are guaranteed to be called" -- AFAIK, yes. "then this leads to a possible leak, right?" -- probably not. onDestroy() will be called unless either you have an unhandled exception or your process is terminated.Psychoneurotic
U
20

First of all let's understand what's going on with the documentation you quoted.

The following commands show git blame output of Activity.java file in AOSP:

$ cd $AOSP/frameworks/base
$ git blame ./core/java/android/app/Activity.java

The relevant part of the output:

9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  363)  * <p>Note the "Killable" column in the above table -- for those methods that
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  364)  * are marked as being killable, after that method returns the process hosting the
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  365)  * activity may killed by the system <em>at any time</em> without another line
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  366)  * of its code being executed.  Because of this, you should use the
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  367)  * {@link #onPause} method to write any persistent data (such as user edits)
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  368)  * to storage.  In addition, the method
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  369)  * {@link #onSaveInstanceState(Bundle)} is called before placing the activity
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  370)  * in such a background state, allowing you to save away any dynamic instance
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  371)  * state in your activity into the given Bundle, to be later received in
550116576 (RoboErik                        2014-07-09 15:05:53 -0700  372)  * {@link #onCreate} if the activity needs to be re-created.
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  373)  * See the <a href="#ProcessLifecycle">Process Lifecycle</a>
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  374)  * section for more information on how the lifecycle of a process is tied
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  375)  * to the activities it is hosting.  Note that it is important to save
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  376)  * persistent data in {@link #onPause} instead of {@link #onSaveInstanceState}
5c40f3fcc (Daisuke Miyakawa                2011-02-15 13:24:36 -0800  377)  * because the latter is not part of the lifecycle callbacks, so will not
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  378)  * be called in every situation as described in its documentation.</p>
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  379)  *
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  380)  * <p class="note">Be aware that these semantics will change slightly between
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  381)  * applications targeting platforms starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  382)  * vs. those targeting prior platforms.  Starting with Honeycomb, an application
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  383)  * is not in the killable state until its {@link #onStop} has returned.  This
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  384)  * impacts when {@link #onSaveInstanceState(Bundle)} may be called (it may be
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  385)  * safely called after {@link #onPause()} and allows and application to safely
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  386)  * wait until {@link #onStop()} to save persistent state.</p>
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  387)  *
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  388)  * <p>For those methods that are not marked as being killable, the activity's
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  389)  * process will not be killed by the system starting from the time the method
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  390)  * is called and continuing after it returns.  Thus an activity is in the killable
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  391)  * state, for example, between after <code>onPause()</code> to the start of
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  392)  * <code>onResume()</code>.</p>

Note that the paragraph that discusses post-Honeycomb behavior was added by Dianne Hackborn on 2010-12-07, whereas the enclosing paragraphs date back to 2009-03-03.

What it tells us is that Dianne added the new paragraph without updating the rest of the javadoc, therefore the contradiction. Unfortunately, this is not rare in Android.

To your questions:

1) On post-Honeycomb versions of Android both onResume() and onStop() are guaranteed to be called (as stated by Dianne Hackborn in her addition to Activity's javadoc).

2) On pre-Honeycomb only onPause() is guaranteed to be called (as stated by the earlier version of Activity's javadoc)

3,4,5) onDestroy() will not be called only if the process hosting the entire application is killed. When the process is killed, all resources allocated to it are freed, therefore there is no risk of memory leak in this case.

Important note: since releasing resources in onDestroy() will not cause memory leak, it might look like a good idea to put all the "releasing" code there. However, it is rarely the optimal approach. Why? Read below.

When Activity goes to background it is stopped, but not destroyed (usually). Activity can remain in this "stopped" state for quite a long time, and will be started again if the user returns to the application. If you release resources in onDestroy(), which is not called by default when Activity goes to background, the Activity will hold to these resources while in stopped state, thus causing higher amount of resources to be consumed by your app in background state.

When Android runs out of memory, it starts killing the processes in order to free the memory consumed by them. One of the most important considerations taken into account when choosing which processes to kill is their resource consumption. Thus, if your app holds to resources while in background stopped state, it will have higher chance of being killed by Android.

In addition, we, the developers, must make sure that we make the best apps for our users. Application that consumes non-minimal amount of user phone's resources and battery while in background is not a good application. And the users will know about it!

Therefore, I strongly advice releasing all resources in onStop() method. I don't usually overwrite onDestroy() methods in Activities and Fragments at all.

Corollary: As pointed out by @Juan in his comment, the above important note has an equally important, but not so evident corollary: onStart() should be the only method in which resources are being allocated. Whatever your definition of "resources" is, neither onCreate() nor onResume() should allocate these resources.

Uredo answered 16/4, 2017 at 15:34 Comment(4)
One additional consideration to maintain code consistent. Resources acquired in onCreate() should be released in onDestroy(). Resources acquired in onResume() should be released in onPause(). The same for onStart() - onStop().Seng
@Juan, this is very important consideration that I forgot to mention explicitly. What I meant to say is that neither onCreate() nor onResume() should allocate any resources at all. I will edit the answer. ThanksUredo
@Seng I disagree with your statement because for example, if you acquire the GPS in onCreate and your application goes in a pause state, it will keep sensing the GPS, which you do NOT want in most cases.Wolfhound
@CaptainObvious I think your example doesn't contradict the rule. If you are pausing the GPS in onPause() and and resuming it in onResume() your are following the rule. You may be initializing the GPS Sensing in onCreate, and perhaps you don't need to do any cleanup when the activity is destroyed. But if you had to clean up any initialization done in onCreate() the first place to think of would be onDestroy(). And this is a general rule. I am sure the are boundery situations where this doesn't apply. Also see Vasiliy's answer where he says that resources should be allocated in onStart().Seng
S
3

I think Vasily provided a great answer. Still there is one small but important point that is missing there related to

  1. Is it true that when Android kills the process that the resources will be destroyed and no leak can occur (even when we did not explicitly released the resource?).

The answer depends on what exactly you are afraid of. As Vasily pointed out Android (being based on Linux) is a modern safe OS which gurantees that when process is killed, all the memory will be released, moreover all open files, network connections etc. will also be properly closed. So there will be no resource leak in a usual sense.

Still there is one potentially problematic scenario. Assume you have some logic you perform when you release some resource i.e. something you would put into a destructor in C++ or in an implementation of Closeable.close in Java. For example, you have a cache in memory that delays writes to a file with batching. Normally you flush that in-memory cache in onDestroy and everything works fine. But when process is forcibly killed by the Android, onDestroy might be not called and your flushing logic is not executed and thus you might loose some data and/or have invalid state in your persistent storage. So if you have something like this, you should ensure consistency by performng such logic in a more reliable place: onPause or onStop.

Sane answered 16/4, 2017 at 20:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.