真机调试悬浮窗关不掉,是因为hbuilderx标准基座的调试浮层无法用js关闭;必须切换自定义调试基座,并确保插件导出hide方法、在onhide中清理、避免内存泄漏。

真机调试时悬浮窗关不掉,是因为用了标准基座
uni-app 真机运行时看到的“悬浮窗”,根本不是你代码里写的悬浮窗,而是 HBuilderX 调试器自带的调试浮层(含控制台、性能监控、网络请求等)。它只在「标准调试基座」下出现,且无法用 JS 关闭。
常见现象:uni.hideToast()、uni.closeDialog() 都无效;插件调用 hide() 方法没反应;甚至杀掉 App 重进,浮层还在。
- 必须切换到「自定义调试基座」——只有它才真正加载你配置的原生插件和 manifest 权限,也才能让悬浮窗逻辑走通
- HBuilderX 中点击菜单「运行」→「运行到手机或模拟器」→ 右侧勾选「使用自定义调试基座」,再选择你制作好的基座
- 未制作过自定义基座?需先在「发行」→「原生App-云打包」→「制作自定义调试基座」,耗时约 5–10 分钟
- 制作完成后,务必重启 HBuilderX 并重新连接手机,否则旧基座缓存仍会生效
自定义基座里悬浮窗仍关不掉,检查插件是否暴露了 hide 方法
很多开源悬浮窗插件(如 dh-float-window、Ba-FloatWindow)只实现了 show() 和 checkPermission(),压根没导出 hide() 或 destroy() 方法。JS 层调用 floatWindow.hide() 会静默失败,控制台也不报错。
验证方式:在插件 JS 层加一行 console.log('hide called'),再调用看看有没有输出;没有输出,说明方法没被注册或拼写错误。
- 检查插件
index.js或main.js是否导出了hide方法,且与uni.requireNativePlugin()返回对象的属性名一致 - 部分插件用
close()或remove()命名,而非hide(),需查其文档或 Java/UTS 源码 - 若插件无隐藏方法,可在原生层调用
windowManager.removeView(view)实现,但需确保传入的是同一 view 实例(不能每次show()都新建)
Android 11+ 上调用 hide() 后窗口还在,可能是权限被系统回收
即使插件正确执行了 removeView(),Android 11+ 也可能因后台限制自动重建窗口。这不是代码 bug,而是系统行为:当应用进入后台后,TYPE_APPLICATION_OVERLAY 窗口可能被系统强制销毁,但权限状态仍为已授权,导致下次唤起时“看起来像没关”。
典型表现:App 切后台再切回前台,悬浮窗又出现了,且 checkPermission() 返回 true。
- 解决方案不是反复调用
hide(),而是在onHide()生命周期里主动清理:调用插件hide()+ 清除本地标记(如uni.setStorageSync('floatShown', false)) - 在
onShow()里加判断:仅当floatShown === true且checkPermission() === true时才show() - 避免在
onUnload()或定时器里调用hide()—— 这些时机 view 可能已被系统回收,调用无效
关闭悬浮窗前忘记清理 WindowManager 引用,会导致内存泄漏
原生插件里如果只是调用 windowManager.removeView(view),但没置空 view 和 windowManager 的强引用,Java 层对象无法 GC,多次开关后 App 内存持续上涨,最终 ANR。
这问题在真机调试时极难察觉,因为调试浮层本身就在吃内存,容易掩盖真实泄漏点。
- 插件 Java/UTS 层应在
hide()方法末尾执行view = null、windowManager = null - 若插件封装了
show()时 new 出的LayoutParams,同样要一并置空 - 真机测试建议用 Android Studio 的 Profiler → Memory 工具抓一次完整开关流程,看是否有
FloatWindowView类实例数持续增长











