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 世界里,权限决定归属,归属决定命运。

相关推荐
alexhilton3 小时前
灵活、现代的Android应用架构:完整分步指南
android·kotlin·android jetpack
hnlgzb5 小时前
build.gradle中的dependencies 中API
android
xiaguangbo6 小时前
rust slint android 安卓
android·linux·rust
lichong9516 小时前
【大前端++】Android studio Log日志高对比度配色方案
android·java·前端·json·android studio·大前端·大前端++
00后程序员张8 小时前
iOS 开发环境搭建完整指南 Xcode 安装配置、iOS 开发工具选择、ipa 打包与 App Store 上架实战经验
android·macos·ios·小程序·uni-app·iphone·xcode
顾林海8 小时前
揭秘Android编译插桩:ASM让你的代码"偷偷"变强
android·面试·性能优化
雨白8 小时前
初识协程: 为什么需要它以及如何启动第一个协程
android·kotlin
文阿花9 小时前
flutter 3.22+ Android集成高德Flutter地图自定义Marker显示
android·flutter
豆豆豆大王10 小时前
Android studio图像视图和相对布局知识点
android·ide·android studio
我命由我1234510 小时前
Android 实例 - Android 圆形蒙版(Android 圆形蒙版实现、圆形蒙版解读)
android·java·java-ee·android studio·安卓·android-studio·android runtime