已检查isFinishing()仍触发WindowManager$BadTokenException问题排查
核心原因
竞态条件导致状态判断失效
isFinishing只是瞬间状态判断,从校验通过到执行.show()的间隙里,Activity可能已经被系统回收或触发销毁流程——此时Activity的WindowToken已失效,Dialog无法绑定到Window,从而抛出异常。这种场景多发生在用户快速操作(如连续按返回键)或系统内存紧张时,手动模拟很难复现。生命周期状态判断不全面
isFinishing仅能识别主动销毁流程,无法覆盖所有无效状态:比如Activity已调用onDestroy但未完全释放资源、处于onStop后的不可见状态,或是系统配置变更(如屏幕旋转)时旧Activity实例未销毁但已无法显示Dialog的场景。自定义Dialog的隐性延迟
即便没有后台线程操作,也要检查ComponentPopup.Builder和createPopup()内部是否存在主线程延迟逻辑(比如用post()做初始化),这类延迟会导致show()执行时Activity状态已发生变化。
解决办法
补充全面的状态校验
替换单一的isFinishing判断,结合多个生命周期状态确保Activity处于可显示Dialog的状态:private fun someFun(headerTitle: String, message: String) { // 确保Activity未销毁、未进入销毁流程且处于可见状态 if (isFinishing || isDestroyed || !isResumed) return ComponentPopup.Builder(this) .setTitleText(headerTitle) .setMessageText(message) .setPositiveButtonText("Some text") .setNegativeButtonText("Some text") .createPopup() .show() }若使用Jetpack Lifecycle组件,可更优雅地判断:
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED).not()) return添加异常捕获兜底
极端场景下(如系统强制回收Activity)无法完全避免状态变化,直接捕获异常防止崩溃:private fun someFun(headerTitle: String, message: String) { if (isFinishing || isDestroyed || !isResumed) return val popup = ComponentPopup.Builder(this) .setTitleText(headerTitle) .setMessageText(message) .setPositiveButtonText("Some text") .setNegativeButtonText("Some text") .createPopup() try { popup.show() } catch (e: WindowManager.BadTokenException) { // 仅记录日志,无需额外处理 } }检查自定义Dialog的实现
确认ComponentPopup内部无延迟初始化逻辑,确保Dialog使用的Context是当前Activity的有效实例,不要持有旧Activity引用;若需在Activity销毁后显示,可改用Application Context并配置悬浮窗权限(不推荐,会影响用户体验)。
内容的提问来源于stack exchange,提问作者Ömer Can Baltacı

