Android WindowManager$BadTokenException异常:Dialog使用合理性问询
解决android.view.WindowManager$BadTokenException:token null无效问题
问题核心分析
从异常调用栈可以明确:触发报错的并非你自定义的Dialog,而是系统内置的FloatingActionMode(即长按文本等操作弹出的悬浮工具栏)。该组件尝试显示时,对应的Activity窗口已无效,导致WindowManager无法添加窗口。
你当前仅用isFinishing判断存在局限性,以下场景会导致Activity窗口失效但isFinishing未触发:
- Activity已进入
onDestroy阶段,但生命周期状态标记未及时更新 - 内存不足时Activity被系统回收,代码仍持有引用并触发UI操作
- 异步回调触发时,Activity已退至后台,窗口token已失效
你的Dialog代码优化建议
虽然当前异常与Dialog无关,但你的showDialog方法可优化以规避同类问题:
- 结合
isDestroyed()(API 17+)覆盖更多销毁场景:fun showDialog(title: String, contents: String) { if (!isFinishing && !isDestroyed()) { val dialog = Dialog(this) // 绑定Dialog到当前Activity,确保生命周期同步 dialog.setOwnerActivity(this) dialog.show() } } - 若
showDialog在异步回调中调用,需额外判断Activity可见性:if (!isFinishing && !isDestroyed() && isVisible) { // 执行Dialog显示逻辑 }
针对FloatingActionMode异常的解决方案
根据调用栈,需从系统ActionMode的启动逻辑入手:
- 全局拦截无效状态下的ActionMode启动:在BaseActivity中重写相关方法:
override fun startActionMode(callback: ActionMode.Callback): ActionMode? { if (!isFinishing && !isDestroyed() && window.decorView.isAttachedToWindow) { return super.startActionMode(callback) } return null } override fun startActionMode(callback: ActionMode.Callback, type: Int): ActionMode? { if (!isFinishing && !isDestroyed() && window.decorView.isAttachedToWindow) { return super.startActionMode(callback, type) } return null } - 拦截触发ActionMode的用户操作:对页面中可触发长按选中的视图(如TextView、列表项),在事件回调中先判断Activity状态:
someTextView.setOnLongClickListener { if (!isFinishing && !isDestroyed()) { // 处理长按逻辑 true } else { false } }
总结
- 当前异常由系统
FloatingActionMode触发,而非自定义Dialog,但Dialog的状态判断逻辑可优化 - 结合
isFinishing、isDestroyed()和window.decorView.isAttachedToWindow三个条件,能更精准判断Activity窗口有效性 - 重写Activity的
startActionMode方法,从源头拦截无效状态下的系统工具栏启动
内容的提问来源于stack exchange,提问作者JeongDev
相关产品推荐
相关产品推荐

