Activity.onStop调用WindowManager.addView展示悬浮窗报错如何解决?
问题根因
- 触发时机问题:Android 7.1及以下版本中,Activity执行
onStop回调时,系统已经回收了当前Activity关联的Window token,此时如果悬浮窗使用的是Activity上下文获取的WindowManager,调用addView时就会因为token失效抛出异常 - 小米ROM适配问题:Redmi 4X搭载的MIUI对后台悬浮窗展示做了额外的权限拦截逻辑,即使
Settings.canDrawOverlays返回true,在应用退后台的瞬间添加悬浮窗也会被ROM拦截触发该异常 - 现有代码的校验逻辑只判断了悬浮窗权限,没有校验当前WindowManager的有效性、以及应用是否处于可添加悬浮窗的状态
修复方案
1. 替换WindowManager的上下文来源
全局使用Application上下文获取WindowManager实例,不要绑定Activity生命周期的上下文,避免token随Activity销毁失效:
// 初始化悬浮窗时用ApplicationContext拿WindowManager private val windowManager = context.applicationContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
2. 调整悬浮窗添加的校验逻辑
除了悬浮窗权限校验,额外增加异常捕获、以及View重复添加判断:
fun show() { if (!Settings.canDrawOverlays(context.applicationContext)) { return } // 捕获添加过程的异常,避免崩溃 runCatching { if (floatView.parent == null) { windowManager.addView(floatView, layoutParams) } }.onFailure { // 异常时可做降级逻辑,比如短延迟重试、记录日志 it.printStackTrace() } }
3. 调整触发时机
如果业务允许,把悬浮窗触发逻辑从onStop调整为onPause回调,Android 7.1及以下版本中onPause执行时Activity的Window token还未被回收,添加成功率更高:
override fun onPause() { super.onPause() // 额外判断Activity是否正在销毁 if (prefsManager.isShowOverlay() && !isFinishing) { floatingWindow.show() } }
4. 小米ROM特殊适配
针对小米7.x系统的机型,添加悬浮窗之前额外校验应用是否在前台,退后台后如果需要展示悬浮窗可以延迟500ms再尝试添加,避开ROM的拦截窗口期。
复现说明
自有设备无法复现是因为该问题仅出现在Android 7.0~7.1.2版本的小米定制ROM上,原生Android系统以及更高/更低版本的MIUI都没有这个拦截逻辑。
内容的提问来源于stack exchange,提问作者user924
相关产品推荐
相关产品推荐

