返回

Android 10及更高版本 Dialog子窗口创建后无法成功拦截Activity事件的问题分析

Android

Android 10 及更高版本下 Dialog 问题概述

在 Android 10 及更高版本中,当在一个 Activity 中创建 Dialog 子窗口时,子窗口无法成功拦截 Activity 事件。这可能会导致 Activity 中的某些事件无法正常响应,例如按返回键时无法正确关闭 Activity。

问题分析

为了分析这个问题,我们首先需要了解 Android 10 及更高版本中 Dialog 子窗口创建过程的改变。在 Android 9 及更低版本中,Dialog 子窗口是直接添加到 Activity 的 WindowManager 中的。但是,在 Android 10 及更高版本中,Dialog 子窗口不再直接添加到 Activity 的 WindowManager 中,而是添加到一个新的 DialogManager 中。

DialogManager 是一个负责管理所有 Dialog 子窗口的系统服务。它负责创建、显示、隐藏和销毁 Dialog 子窗口。当一个 Dialog 子窗口被创建时,DialogManager 会将其添加到一个 Dialog 队列中。当 Dialog 子窗口需要显示时,DialogManager 会将其从 Dialog 队列中取出,并添加到 Activity 的 WindowManager 中。

当用户在 Activity 中按返回键时,Activity 会调用 onKeyDown() 方法。在 Android 9 及更低版本中,onKeyDown() 方法中,Activity 会直接处理返回键事件。但是,在 Android 10 及更高版本中,onKeyDown() 方法中,Activity 不会直接处理返回键事件,而是会将返回键事件传递给 DialogManager。DialogManager 会检查 Dialog 队列中是否有 Dialog 子窗口正在显示。如果有,DialogManager 会将返回键事件传递给正在显示的 Dialog 子窗口。

如果 Dialog 子窗口没有成功拦截返回键事件,那么返回键事件就会传递给 Activity。Activity 会调用 onBackPressed() 方法来处理返回键事件。在 onBackPressed() 方法中,Activity 可能会关闭自己或者执行其他操作。

解决方案

为了解决这个问题,我们可以使用 FLAG_ACTIVITY_NEW_TASK 标记来创建 Dialog 子窗口。FLAG_ACTIVITY_NEW_TASK 标记可以使 Dialog 子窗口成为一个新的任务。当 Dialog 子窗口成为一个新的任务时,它就不会被添加到 Activity 的 WindowManager 中,而是会直接添加到 DialogManager 中。这样,Dialog 子窗口就可以成功拦截 Activity 事件了。

以下是在 Android 10 及更高版本中创建 Dialog 子窗口的代码示例:

Dialog dialog = new Dialog(this, R.style.Theme_AppCompat_Dialog);
dialog.setContentView(R.layout.dialog_layout);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_ACTIVITY_NEW_TASK);
dialog.show();

总结

在 Android 10 及更高版本中,Dialog 子窗口创建后无法成功拦截 Activity 事件的问题是由于 Dialog 子窗口不再直接添加到 Activity 的 WindowManager 中,而是添加到一个新的 DialogManager 中造成的。为了解决这个问题,我们可以使用 FLAG_ACTIVITY_NEW_TASK 标记来创建 Dialog 子窗口。FLAG_ACTIVITY_NEW_TASK 标记可以使 Dialog 子窗口成为一个新的任务。当 Dialog 子窗口成为一个新的任务时,它就不会被添加到 Activity 的 WindowManager 中,而是会直接添加到 DialogManager 中。这样,Dialog 子窗口就可以成功拦截 Activity 事件了。