在一个叫Android宇宙的世界里,有一个窗口王国,由窗口管理员大人(WindowManagerService) 统治。这个王国分为两个平行世界:
1️⃣ 应用世界(Activity宇宙)
- 每个Activity都是一栋会倒塌的大楼(生命周期有限)
- 普通Dialog是楼里的临时展板(依附于大楼)
2️⃣ 系统世界(Global宇宙)
- 永恒存在的天空之城(系统级空间)
- 系统Dialog是悬浮飞艇(独立存在)
案件重现:神秘不消失的Dialog
java
// 嫌疑人代码片段
public class SystemAppActivity extends Activity {
private Dialog ghostDialog; // 幽灵对话框
void createGhostDialog() {
ghostDialog = new Dialog(getApplicationContext()); // 关键点1:使用应用级Context
Window window = ghostDialog.getWindow();
// 关键点2:开启平行宇宙通道
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
ghostDialog.setContentView(R.layout.ghost_dialog);
ghostDialog.show(); // 飞艇升空
}
@Override
protected void onDestroy() {
super.onDestroy();
// 大楼倒塌了,但飞艇还在天上飞!
}
}
解密1:窗口王国的"平行宇宙"法则
宇宙通行证(Token机制)
java
// 普通Dialog:使用Activity Token
public class Dialog {
Dialog(Activity activity) {
mWindow = new PhoneWindow(activity); // 绑定Activity Token
}
}
// 系统Dialog:使用系统级Token
void setSystemWindowType() {
mWindow.setType(TYPE_APPLICATION_OVERLAY); // 激活系统Token
}
通行证类型 | 绑定对象 | 生命周期规则 |
---|---|---|
Activity Token | 具体Activity | 大楼塌=展板毁 |
系统级Token | 全局WindowManager | 与任何大楼无关 |
解密2:窗口管理员的三本登记册

当Activity大楼倒塌时:
java
// WMS的清理代码
void removeActivity(IBinder activityToken) {
// 只查找应用窗口册
List<Window> appWindows = findWindowsByToken(activityToken);
for (Window w : appWindows) {
w.destroy(); // 只销毁普通展板
}
// 系统窗口册完全不受影响!
}
解密3:飞艇的永恒能源系统
系统Dialog为何能永生?因为它的能量来源不同:
能源类型 | 普通Dialog | 系统Dialog |
---|---|---|
动力源 | Activity的View树 | 独立的SurfaceFlinger渲染 |
控制中心 | Activity的WindowManager | 全局WindowManagerGlobal |
生命周期开关 | onDetachedFromWindow | 手动dismiss()或系统回收 |
关键代码证据:
java
// SurfaceFlinger 接收渲染指令
void handleMessage(const Message& message) {
case SYSTEM_WINDOW: // 系统窗口类型
createIndependentSurface(); // 创建独立渲染层
break;
case APP_WINDOW: // 应用窗口类型
linkToActivitySurface(); // 链接到Activity
break;
}
终极解密:平行宇宙的裂缝
当设置了TYPE_APPLICATION_OVERLAY
时,发生了维度跃迁:
java
// WindowManagerService.java
public int addWindow(...) {
if (type == TYPE_APPLICATION_OVERLAY) {
// 进入系统宇宙的裂缝
mSystemWindows.add(window);
attachToSystemSession(); // 绑定到系统会话
// 断开与Activity的连接
window.disconnectFromActivity();
return SUCCESS;
}
// ...普通窗口处理逻辑
}
这时Dialog获得三大超能力:
- 空间穿越:脱离Activity的View树体系
- 时间静止:不再响应Activity生命周期
- 永恒存在:由全局WindowManager直接管理
案件解决方案
要避免幽灵Dialog,必须手动关闭平行宇宙通道:
java
@Override
protected void onDestroy() {
if (ghostDialog != null) {
// 关闭维度通道的关键操作
ghostDialog.dismiss();
// 额外防护(Android 8.0+需要)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ghostDialog.getWindow().setType(TYPE_APPLICATION); // 降维操作
}
}
super.onDestroy();
}
平行宇宙生存法则
场景 | 正确做法 | 危险做法 |
---|---|---|
创建系统Dialog | 使用Application Context | 使用Activity Context |
显示时机 | 检查hasActivity==false时使用 | 在后台Service直接显示 |
关闭机制 | 双保险:onStop+onDestroy关闭 | 依赖Activity自动关闭 |
权限管理 | 动态请求SYSTEM_ALERT_WINDOW | 忘记声明权限 |
内存泄漏防护 | WeakReference + 生命周期监听 | 直接持有Activity引用 |
💡 宇宙真理:系统级Dialog是"跳出三界外,不在五行中"的存在,获得超能力的同时也失去了Activity的保护伞,开发者必须自己承担管理责任!