Dialog不消失之谜——Android窗口系统的"平行宇宙"

在一个叫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获得三大超能力:

  1. 空间穿越:脱离Activity的View树体系
  2. 时间静止:不再响应Activity生命周期
  3. 永恒存在:由全局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的保护伞,开发者必须自己承担管理责任!

相关推荐
jian1105814 分钟前
android java转kotlin,kotlin转java
android·java·kotlin
俩个逗号。。14 分钟前
android 后台应用申请音频焦点失败
android·音视频
summerkissyou198718 分钟前
Android-车机高通蓝牙电话-音频收发流程
android·音视频
TheNextByte119 分钟前
如何导入和导出Android联系人(4 种可行方法)
android·gitee
晚霞的不甘24 分钟前
C语言利用数组处理批量数据详解
android·c语言·开发语言
2501_9160088926 分钟前
iOS 应用发布流程中常被忽视的关键环节
android·ios·小程序·https·uni-app·iphone·webview
ForteScarlet29 分钟前
Kotlin 2.3.0 现已发布!又有什么好东西?
android·开发语言·后端·ios·kotlin
我的offer在哪里30 分钟前
OpenSL ES 完全指南:移动端高性能音频开发实战
android
村里小码农11 小时前
Android APP之间共享数据
android·contentprovider·contentresolver·android app数据共享
Jerry12 小时前
Navigation 最佳实践
android