Dialog 不消失之谜:一场来自系统底层的 "越狱" 行动

第一章:诡异的现场

小李最近遇到了一件怪事。他在开发一个系统工具类 App 时,给 Dialog 加了一行代码:

java 复制代码
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);

结果发现,当他按 Home 键把 App 切到后台,甚至手动 finish 掉 Activity 时,这个 Dialog 竟然像幽灵一样留在屏幕上!

"这 Dialog 成精了?" 小李盯着屏幕喃喃自语。普通 Dialog 只要 Activity 一销毁就会跟着消失,这个加了特殊 "咒语" 的 Dialog,到底发生了什么?

第二章:侦探登场 ------ 对比实验

为了破解谜团,我们做了两组实验,用代码记录下 Dialog 的 "行为轨迹"。

实验 A:普通 Dialog(乖乖听话型)

java 复制代码
// 普通Dialog的创建过程
Dialog normalDialog = new Dialog(MainActivity.this);
normalDialog.setContentView(R.layout.dialog_normal);
normalDialog.setTitle("我是乖宝宝");

// 显示Dialog
showButton.setOnClickListener(v -> normalDialog.show());

// 销毁Activity的按钮
destroyButton.setOnClickListener(v -> {
    MainActivity.this.finish(); // 销毁Activity
    Log.d("Dialog日志", "Activity已销毁");
});

实验现象:点击销毁按钮后,Activity 消失的同时,普通 Dialog 也跟着不见了。

实验 B:系统级 Dialog(叛逆型)

java 复制代码
// 系统级Dialog的创建过程
Dialog systemDialog = new Dialog(MainActivity.this);
systemDialog.setContentView(R.layout.dialog_system);
systemDialog.setTitle("我要自由");

// 关键咒语:设置系统级窗口类型
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    systemDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
} else {
    systemDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}

// 显示Dialog(需要先申请悬浮窗权限)
showSystemButton.setOnClickListener(v -> {
    if (Settings.canDrawOverlays(MainActivity.this)) {
        systemDialog.show();
    }
});

// 同样的销毁按钮
destroyButton.setOnClickListener(v -> {
    MainActivity.this.finish();
    Log.d("Dialog日志", "Activity已销毁");
});

实验现象:点击销毁按钮后,Activity 消失了,但系统级 Dialog 依然霸占着屏幕,甚至能在其他 App 上显示!

第三章:线索追踪 ------ 谁在管理 Dialog?

我们把 Android 系统想象成一个大型公寓楼

  • Activity 是一个个住户房间

  • 普通 Dialog 是房间里的家具(衣柜、桌子),必须放在房间里

  • WindowManagerService(WMS) 是公寓管理员,负责管理所有房间和公共区域

  • TYPE_APPLICATION_OVERLAY 是 "公共区域通行证"

普通 Dialog 的 "户口" 挂在 Activity 名下(通过 Context 关联),就像家具的所有权属于房间主人。当房间被拆除(Activity 销毁),管理员 WMS 会通知家具一起被清走。

但加上TYPE_APPLICATION_OVERLAY后,Dialog 相当于办理了 "户口迁移":

  1. 它从 "房间私有财产" 变成了 "公寓公共设施"(比如楼道里的公告栏)
  2. 管理权从房间主人(Activity)移交到了公寓管理员(WMS)
  3. 即使原来的房间拆了(Activity 销毁),只要管理员没收到拆除通知,公告栏就会一直存在

第四章:底层原理 ------ 窗口的 "身份牌"

Android 中所有可见元素都是 "窗口"(Window),每个窗口都有唯一的type属性,这相当于窗口的 "身份牌":

身份牌类型 归属权 生命周期特点
普通应用窗口(默认) 所属 Activity 随 Activity 销毁而销毁
TYPE_APPLICATION_OVERLAY 系统 WMS 独立存在,需手动销毁

当设置TYPE_APPLICATION_OVERLAY后:

  1. Dialog 的窗口被纳入系统窗口管理体系
  2. 它的生命周期不再与创建它的 Activity 绑定
  3. 只有调用dialog.dismiss()或系统重启时,WMS 才会销毁这个窗口
  4. 这也是为什么需要申请SYSTEM_ALERT_WINDOW权限 ------ 系统要限制这种 "不受控" 的窗口

第五章:真相大白

Dialog 不消失的秘密,其实是一场成功的 "权限升级":

  • 普通 Dialog 是 Activity 的 "附属品",主人不在了它就必须离开

  • 系统级 Dialog 通过TYPE_APPLICATION_OVERLAY获得了 "独立居住权",成为系统直接管理的 "常住居民"

就像现实中:如果你在自己店里挂海报(普通 Dialog),店铺关门海报就得取下;但如果你在商场公共区域挂广告牌(系统级窗口),即使你店铺关门了,广告牌只要没到期就会一直挂着。

破案总结

要让系统级 Dialog 乖乖听话,必须手动 "遣散" 它:

java 复制代码
// 在Activity销毁前,主动销毁系统级Dialog
@Override
protected void onDestroy() {
    super.onDestroy();
    if (systemDialog != null && systemDialog.isShowing()) {
        systemDialog.dismiss();
    }
}

从此,小李的 Dialog 再也不会 "闹鬼" 了。这个故事告诉我们:在 Android 世界里,权限决定归属,归属决定命运。

相关推荐
阿巴斯甜12 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker13 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952714 小时前
Andorid Google 登录接入文档
android
黄林晴15 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android