【问题标题】:Release resources in onPause instead of onDestroy在 onPause 而不是 onDestroy 中释放资源
【发布时间】:2017-09-10 18:40:21
【问题描述】:

这是关于POST-honeycomb(即Android 3.0+),下面的引用来自https://developer.android.com/reference/android/app/Activity.html

根据生命周期,onStop 和 onDestroy 是可杀死的,这意味着:

请注意上表中的“Killable”列——对于那些方法 被标记为可杀死的,在该方法返回之后 托管活动的进程可能随时被系统杀死 没有另一行代码被执行

  1. 换句话说,onStop(以及在此事件之前发生的其他事件)保证会被调用,但是在方法返回的那一刻,进程可能会死掉,因此 onDestroy 是 不保证会被调用。

    另一句话说:

    对于那些没有被标记为可杀死的方法, 活动的进程不会被系统从 方法被调用并在它返回后继续的时间。

    紧随其后

    因此,活动处于 可杀死 状态,例如,在之后 onPause() 到 onResume() 的开始。

  2. 但这确实对应于上面所说的,除非这仅对应于PRE-honeycombPOST-honeycomb 不是这样,对吧?所以基本上,onPause 和 onStop 都保证会被调用。

  3. 假设我只在 onDestroy 中释放资源,那么这可能会导致泄漏,因为可能不会调用 onDestroy,对吧?

  4. 但是,当进程被 android 本身杀死时,除了是否会发生这种情况(即不调用 onDestroy)?是否有任何其他情况导致 onDestroy 不被调用,从而泄漏资源

  5. 当 Android 终止进程时,资源是否会被销毁并且不会发生泄漏(即使我们没有明确释放资源?)。

请提供这些陈述 (1) (2) (3) (4) (5) 是否正确的详细信息。

【问题讨论】:

  • “所以基本上,onPause 和 onStop 都保证会被调用”——AFAIK,是的。 “那么这会导致可能的泄漏,对吗?” - 可能不是。 onDestroy() 将被调用,除非您有未处理的异常或您的进程已终止。

标签: android android-lifecycle activity-lifecycle


【解决方案1】:

首先让我们了解您引用的文档是怎么回事。

以下命令显示 AOSP 中 Activity.java 文件的 git blame 输出:

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

输出的相关部分:

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>

请注意,讨论蜂窝后行为的段落是 Dianne Hackborn 于 2010 年 12 月 7 日添加的,而封闭的段落可以追溯到 2009 年 3 月 3 日。

它告诉我们的是,Dianne 添加了新段落而没有更新 javadoc 的其余部分,因此存在矛盾。不幸的是,这在 Android 中并不罕见。

对于您的问题:

1) 在后 Honeycomb 版本的 Android 上,onResume()onStop() 都保证会被调用(如 Dianne Hackborn 在她对 Activity 的 javadoc 的补充中所述)。

2) 在 pre-Honeycomb 上,只有 onPause() 保证被调用(如早期版本的 Activity 的 javadoc 所述)

3,4,5) onDestroy() 只有在托管整个应用程序的进程被终止时才会被调用。当进程被杀死时,分配给它的所有资源都被释放,因此在这种情况下不存在内存泄漏的风险。

重要提示:由于在onDestroy() 中释放资源不会导致内存泄漏,因此将所有“释放”代码放在那里似乎是个好主意。但是,它很少是最佳方法。为什么?阅读下文。

Activity 进入后台时,它会停止,但不会被销毁(通常)。 Activity 可以保持这种“停止”状态很长一段时间,如果用户返回应用程序,它将再次启动。如果你释放onDestroy()中的资源,当Activity进入后台时默认不调用,Activity将在停止状态时持有这些资源,从而导致你的应用程序消耗更多的资源背景状态。

当 Android 内存不足时,它会开始杀死进程以释放它们消耗的内存。在选择要杀死的进程时要考虑的最重要的考虑因素之一是它们的资源消耗。因此,如果您的应用在后台停止状态下持有资源,则它被 Android 杀死的可能性会更高。

此外,作为开发者,我们必须确保为用户打造最好的应用。在后台消耗非最少量用户手机资源和电池的应用程序不是一个好的应用程序。用户会知道的!

因此,我强烈建议在onStop() 方法中释放所有资源。我通常不会覆盖ActivitiesFragments 中的onDestroy() 方法。

推论: 正如@Juan 在他的评论中指出的那样,上述重要说明有一个同样重要但不那么明显的推论:onStart() 应该是唯一的资源被分配。无论您对“资源”的定义是什么,onCreate()onResume() 都不应该分配这些资源。

【讨论】:

  • 保持代码一致的另一个考虑因素。在 onCreate() 中获取的资源应该在 onDestroy() 中释放。在 onResume() 中获取的资源应该在 onPause() 中释放。 onStart() - onStop() 也是如此。
  • @Juan,这是我忘记明确提及的非常重要的考虑因素。我的意思是onCreate()onResume() 都不应该分配任何资源。我将编辑答案。谢谢
  • @Juan 我不同意您的说法,因为例如,如果您在 onCreate 中获取 GPS 并且您的应用程序进入暂停状态,它将继续感知 GPS,在大多数情况下您不希望这样做.
  • @CaptainObvious 我认为您的示例与规则不矛盾。如果您在 onPause() 中暂停 GPS 并在 onResume() 中恢复它,则您正在遵守规则。您可能正在 onCreate 中初始化 GPS Sensing,并且可能在 Activity 被销毁时不需要进行任何清理。但是,如果您必须清理在 onCreate() 中完成的任何初始化,首先要考虑的是 onDestroy()。这是一般规则。我确信这是不适用的边界情况。另请参阅 Vasiliy 的回答,他说应该在 onStart() 中分配资源。
【解决方案2】:

我认为 Vasily 提供了一个很好的答案。仍然有一个与

相关的小而重要的点丢失了
  1. 当 Android 杀死进程时,资源是否会被销毁并且不会发生泄漏,这是真的吗(即使我们没有显式释放资源?)。

答案取决于你到底害怕什么。正如 Vasily 指出的那样,Android(基于 Linux)是一种现代安全操作系统,它保证当进程被杀死时,所有内存都将被释放,而且所有打开的文件、网络连接等也将被正确关闭。所以不会出现通常意义上的资源泄露。

仍然存在一种可能存在问题的情况。假设您在释放某些资源时执行了一些逻辑,即您将放入 C++ 中的析构函数或 Java 中 Closeable.close 的实现中的东西。例如,您在内存中有一个缓存,可以通过批处理延迟写入文件。通常你刷新onDestroy 中的内存缓存,一切正常。但是,当进程被 Android 强行杀死时,onDestroy 可能不会被调用并且您的刷新逻辑不会被执行,因此您可能会丢失一些数据和/或在持久存储中具有无效状态。所以如果你有这样的事情,你应该通过在更可靠的地方执行这样的逻辑来确保一致性:onPauseonStop

【讨论】:

    猜你喜欢
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多