在Activity生命周期中,“visibility”是指什么?onPause和onStop有何区别?

3
活动生命周期让我头痛。http://developer.android.com/reference/android/app/Activity.html上的文档在描述可见性概念时非常模糊,以至于我无法确定什么时候调用onStop()onPause()。请看以下两个文档语句的比较:
(从生命周期图表下面摘录) onStart()onStop()方法可以被多次调用,因为活动对用户变得可见和隐藏。
VS
(在带有“可杀死”列的蓝色表格中进一步向下) onPause() 当系统即将恢复先前的活动时调用。
从第一个引用中,我理解的是当活动A被“隐藏”时,将调用onStop(),“隐藏”可能指的是另一个活动B已经恢复并完全覆盖了活动A。
但第二个引用则说明,当另一个活动即将开始恢复时,将调用onPause()。那么这不也会完全隐藏活动A吗?两种情况似乎都意味着活动A变得“隐藏”了,不是吗?根据我的可能错误的解释,onPause()onStop()在相同的情况下被调用。
文档似乎还区分了被隐藏(调用onStop())和部分可见性(调用onPause())。但是何时活动仍然是部分可见的呢?他们是指字面上的部分可见吗?或者当一个活动启动了一个新活动(活动调用startActivityForResult并启动日期选择器活动)覆盖整个屏幕时,一个活动仍然可以被认为是“部分可见”的吗?肯定不会调用onStop吧?它应该随时接收到结果!
所以我在努力弄清楚我没有理解的内容。我知道对onPause的调用是有保证的。那就是当Activity A失去焦点(设备进入睡眠模式、屏幕锁定等)时,不同的Activity B将成为前台(Activity B可能或可能没有由Activity A启动)。但是什么时候会在Activity A上调用onStop()?
这是否取决于已经堆叠在Activity A之上的活动数量?两个不同的“可见性”定义是否在起作用?
对于这堵文字墙感到抱歉,但我真的很沮丧: S
因此问题依然存在:在哪些情况下,一个Activity被视为“隐藏”,从而对其调用了onStop()?
编辑:
我在每个onX方法中都插入了Toast通知,并发现了一些附加的怪异行为:
  1. 点击Home按钮将始终调用onStop()。但启动应用程序不会调用onRestart(),而是调用onCreate()。这对我来说很奇怪,但没关系...
  2. 当"USB大容量存储"活动在主活动之上启动时,将调用onStop()。并且当退出usb存储活动返回到主活动时,将调用onRestart(),而不是调用onCreate()。
  3. 当设备进入睡眠模式并被唤醒时,活动只经历onPause()和onResume()周期。

最后一个点是预期的(尽管我无法使其适应生命周期图表)。但1和2怎么了?

在第一个问题中,我期望在再次启动活动时会调用onRestart()。为什么它会释放该活动并调用onCreate()呢?

请看第二点: 根据文档:当“另一个活动出现在活动前面”时,应调用onPaused()。 当USB存储活动出现时,这不是发生的情况吗? 它没有调用onPause(),而是经历了onStop() - OnRestart()循环!显然,该文档并不认为这是“另一个活动出现在活动前面”的情况。那么到底发生了什么?

你提到你不能信任日志,因为onStop可能不会被调用。这种情况只会在Android对资源非常关键并且决定需要使用你的Activity正在使用的资源时才会发生。大多数情况下,你可以信任日志。 - Juan
根据文档,活动资源的释放将在调用onStop或onPause之后首先发生(在onPause之后进行释放是非常罕见的情况)。图表中从暂停到停止的转换触发器是“活动不再可见”。因此,它似乎与您的帖子不符。 - uhmdown
我可能误解了你的帖子,Juan。也许你只是指的在onPause之后执行解除分配的情况。但是,日志语句可能会告诉我何时在个别情况下调用onStop,但它不会给我理解何时应该或不应该调用它的整体画面。 - uhmdown
你说:“但根据我谷歌搜索的结果,没有保证 onStop() 会被调用”,我只是指出大多数情况下它会被调用。只有在极端资源匮乏的情况下才会发生这种情况。插入一些日志语句,你就能明白了。 - Juan
好的,我尝试了记录一些语句,发现了一些有趣的东西,但还不足以得出结论。请参见上面编辑过的问题。 - uhmdown
2个回答

2
好的,我现在明白了。
1.
第一个要点的关键在于这个链接:

http://code.google.com/p/android/issues/detail?id=2373

这是一个 bug。链接中有一些代码完全解决了新的根活动实例被创建的问题,而不是仅重新启动上次活动(在按下主页按钮之前)。
我将代码放在 onCreate 方法的顶部,在 super.onCreate 调用下方:
if (!isTaskRoot()) {
    final Intent intent = getIntent();
    final String intentAction = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) &&
            intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
        finish(); return;
    }
}

请注意,我在完成后添加了返回语句,以防检测到错误时继续运行onCreate方法的其余部分。
2.和3.
第二点和第三点的关键是这两个链接:

http://answers.oreilly.com/topic/2692-android-programming-understanding-the-activity-life-cycle/

如何制作非全屏幕的Activity

原来,“可见性”真的是字面意思!所以当文档说“另一个Activity出现在前面时”,被挤压的Activity后面仍然部分可见。这意味着Android Activity管理器必须检查被挤压的Activity是否是全屏幕Activity:如果是,就会调用前一个Activity的onStop()方法。如果不是,则会调用前一个Activity的onPaused()方法。

这个解释很容易说明为什么USB存储管理器会导致调用onStop()方法。

这也意味着当设备进入睡眠模式时,Activity Manager认为它是非全屏幕Activity,即使主Activity在技术上完全隐藏在其后面。

(请参见第二个链接,了解如何制作非全屏幕Activity)

有趣的是,下拉窗口(带通知的窗口)不会调用onPause()方法(也不会调用onStop()方法),即使将其视为非全屏幕Activity也是有道理的。这一定是我自己要调查的某种例外情况。

这也意味着onStop()-onRestart()循环可能比onPause()-onResume()循环更常见(尽管两者都必须考虑),因为活动可能更常见是全屏活动(就个人而言,我认为文档表明相反:onPause-onResume更常见,但也许只是我自己的看法)。
此外,这也意味着当主活动启动一个新的全屏活动以获取结果时,主活动将首先停止,等到结果检索活动完成后再重新启动。
所以现在唯一的问题是如何最好地处理已暂停的活动(即它被非全屏活动覆盖)并被释放的情况(尽管这种情况很少出现)。可能会有哪些挑战?
但这超出了本问题的范围。

你有没有找到关于状态栏下拉不触发onPause()的任何信息? - Jon Willis
1
不完全是。我只是将其视为一种特殊情况。我想它只是不在活动生命周期框架内,直接绕过了所有内容。从某种意义上讲,它没有与当前活动交互的必要,因为它只是一个快速的覆盖层,用于检查通知,所以我就这样留下了它。 - uhmdown
如果我决定深入研究AOSP并找到答案,我会在这里发布一个快速评论。 - Jon Willis

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接