共计 2394 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点:Token 管理引发的典型问题
在 Android 开发中,ActivityRecord Token 是 Activity 与系统服务(如 AMS、WMS)通信的桥梁。但许多开发者对它的生命周期管理不够重视,导致以下常见问题:

-
Activity 无法正常销毁 :当 Token 被错误持有时,AMS 无法回收对应的 Activity 实例,表现为
onDestroy()被延迟调用或完全不调用 -
WindowManagerService 报错:在错误的线程使用 Token 进行跨进程调用时,会触发
BadTokenException(典型错误日志:”Unable to add window — token null is not valid”) -
内存泄漏连锁反应:一个泄漏的 Token 会导致对应的 Activity、DecorView 乃至整个视图树无法被 GC 回收
机制解析:Token 的底层工作原理
Binder 驱动的跨进程通信
ActivityRecord Token 本质上是 Binder 代理对象,其序列化 / 反序列化过程如下:
- AMS 通过
ActivityRecord.appToken生成 IBinder 对象 - 通过 Binder 驱动将对象引用转换为扁平化的
Parcel数据 - 客户端进程接收后重建为
IApplicationToken.Stub代理对象
// AOSP 代码片段(ActivityRecord.java)final IApplicationToken.Stub appToken = new Token();
private class Token extends IApplicationToken.Stub {// 实现 WMS 回调接口}
ActivityRecord 的映射关系
在系统服务端,Token 与组件的关系如下图所示:
[Client Process] [System Server]
Activity -----------------> ActivityRecord
| / \
|-> ViewRootImpl AMS WMS
关键点:
- 每个 ActivityRecord 对应一个唯一的 Token
- ViewRootImpl 通过
setView()将 Token 传递给 WMS - Token 的生命周期必须与 Activity 的
onDestroy()同步
代码实践:安全使用 Token 的方案
正确封装 Token 的 Kotlin 示例
class SafeTokenHandler(activity: Activity) {private val weakToken = WeakReference(activity.window?.attributes?.token)
@MainThread
fun performWindowAction(action: (IBinder?) -> Unit) {if (Looper.myLooper() != Looper.getMainLooper()) {throw IllegalThreadStateException("Must call from UI thread")
}
action(weakToken.get())
}
}
LeakCanary 定制检测方案
-
添加依赖:
dependencies {debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9'} -
自定义检测规则:
class TokenLeakDetector : ObjectInspector {override fun inspect(reporter: ObjectReporter) {reporter.whenInstanceOf("android.view.ViewRootImpl") { instance -> val tokenField = instance.javaClass.getDeclaredField("mWindowToken") tokenField.isAccessible = true val token = tokenField.get(instance) as IBinder? if (token != null && token.isBinderAlive) {reporter.reportLeak("ViewRootImpl token leaked", "") } } } }
避坑指南:关键注意事项
线程安全准则
- 永远只在 UI 线程操作 Token(可通过
@MainThread注解强制约束) - 禁止在静态变量或长生命周期对象中持有原始 Token 引用
Configuration 变更处理
当设备旋转等操作触发配置变更时:
- 旧 Activity 的 Token 会随
onDestroy()被回收 - 新 Activity 实例会生成新 Token
- 需要重写
onSaveInstanceState()转移必要数据
override fun onSaveInstanceState(outState: Bundle) {super.onSaveInstanceState(outState)
// 保存与窗口无关的临时状态
outState.putString("KEY_DATA", transientData)
}
进阶思考:Compose 时代的 Token 机制
在 Jetpack Compose 架构下:
- 传统
WindowToken的作用被弱化 LocalView.current提供了替代访问方式- 但底层仍然依赖 Token 进行窗口管理
未来可能的演进方向:
- 窗口令牌与组合树的深度集成
- 声明式 API 替代显式 Token 传递
- 更严格的线程访问限制
总结
理解 ActivityRecord Token 机制需要把握三个维度:
- 跨进程通信:作为 Binder 代理的本质
- 生命周期同步:与 Activity 销毁的强关联
- 线程约束:严格的 UI 线程访问要求
通过弱引用封装、LeakCanary 监控和正确的线程管理,可以避免 90% 以上的 Token 相关问题。在新技术栈中,虽然使用方式可能变化,但核心的进程间通信原理仍然适用。
正文完
