安卓:在什么情况下,弹出对话框会导致调用onPause()方法?

81
从 Android Activities 文档中摘录一段(滚动到“foreground lifetime”行):

一个活动可以经常在前台和后台之间切换,例如当设备进入睡眠状态或者出现对话框时会调用onPause()方法。

我不太理解这句话。在什么情况下会发生这种情况?如果所讨论的对话框的上下文与要显示对话框的活动不同,那么是否只会调用onPause()方法?
编辑:添加代码示例以详细说明我的疑问
根据上述文件中的引用,当以下代码中的AlertDialog(或只是Dialog)被显示时,应该调用我的活动的onPause()方法吗?当对话框被显示时,我应该看到“onPause called”日志条目吗?
但我没有看到发生这种情况。如果我正确地理解了Android生命周期,它也不应该发生!那么,这份文件指的是什么呢?
public class LifeCycleTestActivity extends Activity {

    private static final String TAG = "LifeCycleTest";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Button btn = (Button) findViewById(R.id.button1);

        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Log.d(TAG, "onClick");

                AlertDialog dialog = new AlertDialog.Builder(LifeCycleTestActivity.this).create();
                 dialog.setMessage("You Clicked on the button");
                 dialog.setTitle("Dialog!");
                 dialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
                 dialog.setCancelable(true);
                 dialog.show();


                /*
                Dialog dialog = new Dialog(LifeCycleTestActivity.this);
                 dialog.setTitle("Dialog!");
                 dialog.setCancelable(true);
                 dialog.show();
                */
            }
        });        
    }

    @Override
    protected void onPause() {
        Log.d(TAG, "onPause() called");
        super.onPause();

    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume() called");
    }
}
7个回答

197

onPause() 方法是当你的活动不再处于活动堆栈的顶部时调用的。单独的对话框并不是一个活动,因此不会取代当前位于堆栈顶部的活动,也不会导致其他任何内容暂停。

小写的“dialog”并不一定要由 Dialog 类来实现。例如,通常使用主题设置为对话框的 Activity 来实现对话框。在这种情况下,将对话框作为一个活动显示将导致新的 Activity 处于堆栈顶部,暂停先前存在的内容。


9
很好的答案。我没有想到“对话框作为活动”的概念。而且,只有当对话框位于堆栈顶部时才会调用onPause()的推理也解释了为什么即使通知栏被拉到底部完全遮挡了活动,onPause()也不会被调用。 - curioustechizen
2
@curioustechizen 这里有很多微妙之处... 感谢你的问题(以及 Android 团队这位了不起的专家的答案),我已经能够快速理解为什么我在我的应用程序中观察到了与你完全相同的情况。+1。 - ateiob
1
@hackbod:系统对话框会导致onPause()吗?我在查看棉花糖权限相关的系统对话框时看到了这个问题! - abat
@abat 这可能是因为权限对话框被实现为一个主题设置为对话框的活动。 - curioustechizen
大家好,我有一个问题:在AOSP 10中,当当前Activity处于onStop()状态时,我的对话框不显示。但在Android 6中,同样的状态可以正常工作。请告诉我为什么在Android 10中不显示。 - Sakthivel Appavu

13

我一直在编写涉及对话框的代码,包括你提到的 AlertDialog,而且我也尝试检查当对话框弹出时是否会调用活动的 onPause() 方法,但到目前为止,我的结论是该活动仅保持运行状态,并且没有调用 onPause() 方法

不确定这是否有帮助,但至少现在您知道还有其他人经历了您正在经历的问题 :-)


实际上,如果我正确地理解了生命周期,我相信对话框绝不能触发 onPause()。因此,观察到的行为与预期一致。只是我偶然发现了这条引用和链接的信息。那让我陷入了困惑的境地! - curioustechizen

2
在onPause阶段,活动仍然保持在顶部活动堆栈上是错误的。
使活动处于onPause状态的条件为: - 活动部分可见,例如活动上的对话框。 - 在内存中保留Activity对象,它维护所有状态和成员信息,并保持附加到窗口管理器。 例如,按Home键会导致活动进入onPause()状态,但仍然在堆栈的顶部。
在图1中,Activity3将被销毁并从顶部堆栈中移除。
在图2中,现在任务A进入后台,但Activty X仍然在堆栈的顶部。如果你在这种状态下覆盖了onPause()方法,
图1展示了每个新活动如何向任务添加一个项目。当用户按下回退按钮时,当前活动将被销毁,上一个活动将恢复。
图2展示了两个任务:任务B接收前台用户交互,而任务A在后台等待恢复。

1
在你解释的情况下,Activity 仍然位于堆栈顶部,但任务本身已经转到了后台。基本上,用户不再与你的 Activity 进行交互 - 另一个 Activity(无论是来自你自己的任务还是其他任务)已经出现在前台。这就是被接受的答案试图解释的内容。 - curioustechizen

0

我记得在早期版本的Android生命周期中,当活动没有显示时会调用onPause方法。也就是说,如果您的活动的一部分仍然可见于弹出窗口下方,则不会调用onPause方法。

也许其他专家可以证实这种行为?


关于 onPause() 方法的一点说明是,如果没有任何 Activity 在显示,它会被调用 - 这通常是正确的,但并非严格如此。例如,当您下拉通知栏以完全遮挡您的 Activity 时,仍然不会调用 onPause() 方法。 - curioustechizen

0

@hackbot

onPause() 在您的活动不再位于活动堆栈的顶部时调用。对话框本身不是一个活动,因此不会替换活动堆栈顶部的当前活动,所以不会导致任何暂停。

一切都取决于实现...

什么是对话框?是通过WindowManager添加到显示器上的窗口,所以当它显示时,它位于最上方...(Z顺序)

什么是活动... 是也创建其窗口的“东西”....

当对话框显示或其窗口出现在现有活动的顶部时,它将部分覆盖活动窗口,因此现有活动将进入部分隐藏状态,并且您将从ActivityThread收到对onPause()的调用

但要确保,我们还需要考虑一个因素...

窗口的状态,如果它是一个独立窗口显示在顶部,还是一个子窗口并且其父窗口是一个活动窗口...

所以当我们知道了这些

  • 我们使用的Window.LayoutParams(FLAGS)来添加
  • IBinder用于窗口显示

当窗口彼此显示时,我们将了解活动的行为方式...因为每个窗口都有回调函数,它们由活动或对话框用于管理其状态...

相关组件:

  • android.os.IBinder

  • android.view.Window

  • android.view.Window.Callback

  • android.view.WindowManager

  • android.view.WindowManager.LayoutParams

  • android.view.Display

顺便说一下:

如果你想知道屏幕上的窗口[仅适用于您拥有的进程 - 因为窗口属于进程,而这些进程是沙箱化的 - 每个进程严格地说是一个单独的JVM“ART”],你可以使用反射查看:

  • android.view.WindowManagerImpl
  • android.view.WindowManagerGlobal

0
在我的一些奇怪的经验中,onResume 会与 dialog.setCanceledOnTouchOutside(true); 一起调用,但是 onPause 从未被调用过。
话虽如此,我认为文档可能更关注系统对话框(例如低电量)。

-4

onPause() 方法会在 Activity 进入后台并且 Dialog 或其他 Activity 进入前台时被调用。这是为了给用户正在交互的内容优先级。例如:假设您在应用程序的主屏幕(也是一个 Activity)中,那么主屏幕就处于foreground状态。当您按下某个按钮或弹出对话框时,下一个screen/Activity/Dialog 就会进入foreground状态,而主屏幕则进入background状态,这意味着主屏幕onPause()方法被调用。


另一个Activity进入前台的情况很明显。但我对文档声称当对话框出现时会调用onPause()感到困惑。我已经编辑了我的问题,并附上了代码,解释了我认为文档所声称的内容,但我并没有看到发生这种情况。 - curioustechizen
@techie.curious,是的,也许你是对的。这只会在活动转换时发生,而不是创建对话框时发生。我现在已经检查过了。 - ngesh

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