7、Token 机制:为什么需要 Token?Token 的生成与传递流程
好,咱们今天来聊聊 WindowManager 里一个挺有意思的设计——Token 机制。
说实话,我刚接触 Android 源码那会儿,看到 Token 这个词,第一反应是「这不就是个安全令牌吗?跟 Web 登录的 Token 差不多吧?」后来踩了几个坑才发现,完全不是那么回事。Android 里的 Token,说白了是一把「窗口许可证」。没有它,你的窗口就别想挂到屏幕上。
为什么需要 Token?
你想想看,Android 系统里那么多 App,每个 App 又可能创建一堆窗口。如果谁都能随便往屏幕上扔窗口,那不乱套了吗?
Token 的核心作用就两个:
- 身份校验——证明「我是谁」
- 权限控制——证明「我有资格」
举个例子。我记得有一次排查一个线上 bug,某个第三方 SDK 弹不出对话框。日志里报的是 BadTokenException。当时我一看就明白了——这哥们儿拿了个过期的 Token 去加窗口,系统不认。
Token 本质上是一个 IBinder 对象。它由系统服务(WMS)创建,然后分发给客户端。客户端拿着这个 Token 来请求窗口操作,WMS 会校验这个 Token 是否合法、是否还在有效期内。
核心要点:Token 是 WMS 和客户端之间的「信任凭证」。没有 Token,客户端连跟 WMS 对话的资格都没有。
Token 的生成流程
Token 什么时候生成?嗯,最常见的是在 Activity 启动的时候。
咱们走一遍流程:
- AMS 启动 Activity——ActivityManagerService 决定要启动一个 Activity。
- AMS 向 WMS 申请 Token——AMS 调用 WMS 的
addAppToken方法。 - WMS 创建 Token——WMS 内部创建一个
AppWindowToken对象,里面封装了一个IBinder。 - Token 返回给 AMS——WMS 把这个 Token 返回给 AMS。
- AMS 把 Token 传给 Activity——AMS 通过 Binder 调用,把 Token 传给 ActivityThread。
- ActivityThread 保存 Token——ActivityThread 把 Token 存到 ActivityClientRecord 里。
嗯,这里有个细节。Token 是 IBinder,它本身是一个 Binder 对象。Binder 在进程间传递时,系统会自动处理引用计数。所以 Token 不会因为跨进程就丢失了身份信息。
个人经验:我建议你在调试窗口问题时,先检查 Token 是否为空。很多莫名其妙的窗口显示问题,根源就是 Token 没传过来。
Token 的传递流程
Token 生成之后,怎么传到 ViewRootImpl 手里?这个过程其实挺曲折的。
我画了一张图,帮你理清这个流程:
流程看起来有点长,但核心就一句话:Token 从 WMS 出发,经过 AMS、ActivityThread、Activity、PhoneWindow,最后到达 ViewRootImpl。
每一步都在传递同一个 IBinder 对象。因为 Binder 是跨进程的,所以即使 WMS 在 system_server 进程,Activity 在 App 进程,Token 也能正常工作。
Token 校验
当 ViewRootImpl 调用 addWindow 时,WMS 会做以下几件事:
| 校验步骤 | 说明 |
|---|---|
| Token 非空检查 | Token 是否为 null |
| Token 类型检查 | 是否是 AppWindowToken 或 WindowToken |
| Token 有效性检查 | Token 对应的窗口容器是否还存在 |
| 权限检查 | 调用方是否有权限使用该 Token |
避坑指南:我曾经遇到过一个 case,App 在后台通过 Service 弹窗,结果崩溃了。原因就是 Activity 已经销毁,Token 被 WMS 回收了。这时候再拿 Token 去加窗口,WMS 直接返回 WindowManagerGlobal.ADD_BAD_APP_TOKEN。
Token 的生命周期
Token 不是永久的。它跟 Activity 的生命周期绑定。
- Activity 创建——Token 生成
- Activity 可见——Token 有效
- Activity 销毁——Token 失效
嗯,这里有个特殊情况。如果 Activity 配置了 configChanges,旋转屏幕时 Activity 不会重建,Token 也不会变。但如果是正常的重建流程(比如语言切换),Token 会重新生成。
我记得有一次调试一个平板适配问题,横竖屏切换时 Dialog 消失了。查了半天发现是 Token 变了,但 Dialog 还拿着旧的 Token。解决方案是在 onCreate 里重新创建 Dialog。
Token 的其他用途
除了 Activity 窗口,Token 还用在其他地方:
- 子窗口(SubWindow)——比如 PopupWindow,它的 Token 是父窗口的 Token
- 系统窗口——比如 Toast,它的 Token 是系统级别的 WindowToken
- 输入法窗口——IME 有自己的 Token 体系
说白了,Token 机制是 Android 窗口系统的一道「安检门」。它保证了只有合法的窗口才能被显示,也保证了窗口之间的层级关系不会乱套。
个人建议:如果你在开发中遇到窗口显示异常,第一步就是检查 Token。用 WindowManagerGlobal.getInstance().getWindowSession() 可以拿到当前的 Session,然后看看 Token 是不是还在有效期内。
好了,Token 机制就讲到这里。记住一句话:Token 是窗口的身份证,没有它,寸步难行。