Android 系统 Window 概念理解
Window 是整个 Android View 系统中最基本、核心的概念。本文旨在概括性地介绍 Window 中的一些重要概念,以方便后续对 Window 相关操作的深入学习。
如果理解 Window
在 Android 系统中,任何可视化的界面,都是以 Window 的概念而存在。包括我们日常开发中最常接触到的 Activity、弹窗 Dialog、系统界面固定的状态栏、虚拟按键等,总之,所有的视图(可见或不可见),都是一个个独立的 “窗口”,受到系统 Framework 服务来统一管理。
对于普通开发者而说,处理视图直接相关的是 View 对象;反而对于 Android 系统来说,Window 才是系统管理其内视图的单位。如果我们想新展示一个界面,比如一个 Activity,其本质上是通知系统服务来添加一个新的 Window。
Window 是一个概念,没有一个实质完整的‘类’来描述。对系统来说直接操作的对象是一个 View,也就是一个 ViewTree 里最外层的那个 ViewGroup。
然而 Window 本身并不直接等于 View, View 对象只表示一个具体展示的视图,除此以外,一个 Window 还包含了窗口类型、token、窗口布局等参数,这些共同形成一个完整的 Window 概念。
如果查看 Window
手机通过 adb 连接后,可以通过以下命令查看当前手机中打开的全部窗口:
adb shell dumpsys window | grep -E 'Window #\d{1,} Window'
比如,我的一加手机当前“只打开”了微信应用,查看全部的窗口信息如下:
➜ ~ adb shell dumpsys window | grep -E 'Window #\d{1,} Window'
Window #0 Window{578b2e8 u0 InputMethod}:
Window #1 Window{5164e47 u0 ScreenDecorOverlayBottom}:
Window #2 Window{917eae u0 ScreenDecorOverlay}:
Window #3 Window{526fe2e u0 NavigationBar0}:
Window #4 Window{dd335a7 u0 StatusBar}:
Window #5 Window{4f8a3b4 u0 AssistPreviewPanel}:
Window #6 Window{2564720 u0 DockedStackDivider}:
Window #7 Window{2650353 u0 com.tencent.mm/com.tencent.mm.ui.LauncherUI}:
Window #8 Window{86826e4 u0 net.oneplus.launcher/net.oneplus.launcher.Launcher}:
Window #9 Window{7dc0b56 u0 com.oneplus.wallpaper.LiveWallpaper}:
另外,还可以通过以下命令查看当前聚焦的窗口:
➜ ~ adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'
mCurrentFocus=Window{dd335a7 u0 StatusBar}
mFocusedApp=AppWindowToken{76df2b7 token=Token{e565b6 ActivityRecord{fdcd951 u0 com.tencent.mm/.ui.LauncherUI t3879}}}
Window 的类型
通过 adb 查看手机系统中当前全部的 Window,发现有些窗口是系统常驻的,比如状态栏、导航栏,有些则是属于一个应用的,每个应用可以打开不定数量的窗口。
其中,每个窗口都有一个 int 值来表示窗口类型,整体上分为以下三类:
类型 | 说明 | 范围 |
---|---|---|
APPLICATION_WINDOW | 应用窗口,如 Activity | 1~99 |
SUB_WINDOW | 子窗口,不能单独存在,只能附属在父 Window 中,如 Dialog 等 | 1000~1999 |
SYSTEM_WINDOW | 系统窗口,需要权限声明,如 Toast 和系统状态栏等 | 2000~2999 |
窗口类型的取值范围定义在
frameworks/base/core/java/android/view/WindowManager.java
文件中
Window 的 token
既然全部的 Window 都受到系统统一的管理,那么 Window 间的安全性就是一个很重要的问题了。试想一下,如果其他的应用可以随意在你应用的窗口上绘制更改内容,甚至移除你的应用的窗口,那么系统的安全性将会是极不可靠的。
在 Android 系统中,每个应用窗口窗口对会有其对应的一个 token,也就是所谓的“窗口令牌”。一个窗口的 token 就像是它的合格且唯一的凭证一样,只有带合格 token 的窗口,才能够添加进系统中;只有具有相同 token,你才能够对目标窗口进行操作。
其中,系统类型的窗口(SYSTEM_WINDOW)比较特殊,它通常是由系统所创建,是不需要 token 的。而对于应用窗口来说,token 则是必须的。
为了 token 不能够被轻松地“仿造”,token 对象被设计为 Binder 对象。为什么是 Binder?其实不止是 Window 中,整个 Android 系统大部分的跨进程通信都是依赖了 Binder 的唯一性,把它作为 token 来保证安全性。Binder 的唯一性是指,每个 Binder 实例在系统所有进程中都保持唯一的身份,这是由 Binder 内核驱动来做保证的。这里就不展开细讲了。
因此,当你向系统请求添加或删除一个 Window 时,如果携带的 token 不匹配,将会抛出 BadTokenException
。