2. Window 的概念与分类

说到 Window,很多人第一反应就是「窗口」。嗯,这个理解没错,但在 Android 里,Window 的概念要更底层一些。

我个人的理解是:Window 是承载 View 的容器,也是 View 与系统交互的桥梁。说白了,你在屏幕上看到的任何东西,最终都要通过 Window 来呈现。没有 Window,View 连显示的机会都没有。

我记得刚接触 Android 时,以为 Activity 就是窗口。后来深入源码才发现,Activity 只是管理 Window 的生命周期,真正的绘制和事件分发,全在 Window 里完成。

2.1 Window 的定义

从源码角度看,Window 是一个抽象类,它的唯一实现是 PhoneWindow。你可以把 Window 想象成一个「画板」,View 就是画板上的画。画板本身不负责画什么,但它决定了画的位置、大小、层级。

关键点在于:Window 管理的是屏幕上的像素区域。每个 Window 都有一个 Z-order(层级顺序),系统根据这个顺序决定谁在上层、谁在下层。

核心概念:Window 是 View 的载体,每个 Window 对应一个 Surface,Surface 才是真正在屏幕上绘制的画布。

2.2 Window 的三种分类

Android 把 Window 分成三大类。这个分类不是随便分的,它直接决定了 Window 的权限、显示层级和使用场景。

类型 层级范围 典型场景 权限要求
Application Window 1 ~ 99 Activity、Dialog
Sub Window 1000 ~ 1999 PopupWindow、菜单
System Window 2000 ~ 2999 状态栏、Toast、悬浮窗 需要权限

你可能会问:为什么要有层级范围?

原因很简单——系统需要控制谁能在最上面。比如系统警告弹窗必须盖住所有应用,而普通应用的弹窗不能盖住状态栏。

2.3 Application Window

这是最常见的 Window 类型。每个 Activity 启动时,都会创建一个 Application Window。它的层级最低,范围是 1 到 99。

特点:

  • 必须依附于 Activity 或 Service
  • 不能出现在其他应用之上
  • 不需要额外权限

我在项目中遇到过一个问题:有同事想在 Application 里直接显示一个 Dialog,结果崩溃了。原因就是 Application 没有 Window 环境,Dialog 需要依附一个 Activity 的 Window。

小技巧:如果你需要在非 Activity 场景显示界面,可以考虑使用 System Window 或者创建一个透明的 Activity。

2.4 Sub Window

Sub Window 是「子窗口」,它必须依附于一个父 Window。层级范围是 1000 到 1999。

典型代表:

  • PopupWindow
  • Context Menu
  • Options Menu

Sub Window 的特点是:它的位置和层级都受父 Window 控制。父 Window 移动,子 Window 跟着移动;父 Window 消失,子 Window 也消失。

嗯,这里要注意:Sub Window 的 Z-order 虽然比父 Window 高,但它仍然在父 Window 的「领地」内。你不能让一个 PopupWindow 跑到另一个应用的界面上去。

避坑指南:我曾经在 PopupWindow 里嵌套另一个 PopupWindow,结果触摸事件乱飞。后来发现 Sub Window 的事件分发机制和普通 Window 不一样,嵌套使用要格外小心。

2.5 System Window

System Window 是「系统级窗口」,层级最高(2000 ~ 2999)。它可以在所有应用之上显示。

常见场景:

  • 状态栏、导航栏
  • Toast 提示
  • 悬浮球、悬浮窗
  • 系统级警告弹窗

使用 System Window 需要声明权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

而且从 Android 6.0 开始,这个权限需要用户手动授予。我记得有一次做悬浮球功能,用户反馈说「为什么我的手机上不显示?」——嗯,就是因为没处理权限申请流程。

System Window 的创建方式也略有不同:

WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,  // 注意这个 type
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT
);

重要变化:Android 8.0 之后,推荐使用 TYPE_APPLICATION_OVERLAY 替代旧的 TYPE_SYSTEM_ALERT。前者层级更低一些,但更安全,不会出现「盖住系统关键界面」的问题。

2.6 三种 Window 的核心区别

说白了,区别就三点:

  1. 层级不同:Application Window 在最底层,System Window 在最顶层
  2. 权限不同:System Window 需要额外权限,其他不需要
  3. 生命周期不同:Sub Window 依赖父 Window,父窗口销毁它也得销毁

你想想看,如果你要做一个「全局悬浮球」,用 Application Window 肯定不行,因为它只能在 Activity 范围内显示。用 Sub Window 也不行,因为一旦 Activity 退到后台,悬浮球就没了。所以只能用 System Window。

2.7 知识体系图

下面这张图展示了 Window 分类的核心逻辑:

Window 分类体系 Window(抽象类) PhoneWindow(唯一实现) Application Window 层级 1 ~ 99 Sub Window 层级 1000 ~ 1999 System Window 层级 2000 ~ 2999 Activity / Dialog PopupWindow / 菜单 状态栏 / 悬浮窗 层级越高,显示越靠上。System Window 可覆盖所有应用 Sub Window 必须依附于父 Window,生命周期受父窗口控制

2.8 实际开发中的选择建议

我个人的经验是:能用 Application Window 就别用 System Window。原因很简单——System Window 需要权限,而且用户体验不好。你想想看,一个普通应用突然弹出一个系统级窗口,用户会觉得「这 App 是不是有问题?」

如果你需要做「全局悬浮球」之类的功能,记得做好权限引导。我曾经见过一个 App,申请了悬浮窗权限却不说明用途,结果被用户直接卸载了。

嗯,最后说一句:理解 Window 的分类,是理解 WindowManager 服务的第一步。后面的内容会越来越深入,但基础打牢了,后面就顺了。


公众号:蓝海资料掘金营,微信deep3321