Android12/13 WindowManager报window count超300崩溃如何排查
Android 12/13 窗口数超限崩溃排查指南
核心报错定义
- 报错中提到的300限制和普通View总数、列表展示项数无关,是Android 12开始新增的单进程顶级窗口数量硬上限:所有通过
WindowManager.addView()添加的根窗口,包括Activity窗口、Dialog、PopupWindow、悬浮窗、Toast、自定义浮层、第三方SDK浮层等,全部计入该计数,单进程持有窗口总数超过300就会直接抛出异常。Android 13保留了该限制,仅简化了报错文案。
已捕获异常信息
PopupWindow触发场景堆栈
window count is over max 300 android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:430) android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133) android.widget.PopupWindow.invokePopup(PopupWindow.java:1688) android.widget.PopupWindow.showAtLocation(PopupWindow.java:1408) android.widget.PopupWindow.showAtLocation(PopupWindow.java:1374)
Activity生命周期触发场景堆栈
window count is over max 300 android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:430) android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133) android.app.ActivityThread.handleResumeActivity(ActivityThread.java:5322) android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54) android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) android.app.ActivityThread$H.handleMessage(ActivityThread.java:2438) android.os.Handler.dispatchMessage(Handler.java:106) android.os.Looper.loopOnce(Looper.java:226) android.os.Looper.loop(Looper.java:313) android.app.ActivityThread.main(ActivityThread.java:8663) java.lang.reflect.Method.invoke(Native Method) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Android 13 对应报错文案
window count is over max!!
排查思路
之前仅排查Dialog、PopupWindow组件无法复现属于正常情况,窗口泄漏来源覆盖多类组件,按以下优先级排查效率最高:
- 先搭建全局窗口监控能力
- debug、灰度环境下,通过反射获取
WindowManagerGlobal内部存储所有窗口引用的mViews列表,定时打印当前窗口总数、每个窗口对应的上下文类名、添加位置的调用堆栈;设置阈值告警,当窗口数超过200就上报全量日志,不用等攒到300崩溃再抓现场。 - 优先排查Activity泄漏问题:Activity自身的窗口也计入总数,第二份堆栈在Activity resume阶段触发崩溃,大概率是存在Activity实例泄漏:重点检查路由拦截逻辑是否异常、deeplink跳转是否存在死循环、是否有上下文持有导致Activity无法回收——这类问题会导致反复启动新Activity实例,旧实例无法销毁,攒够300个就会在新Activity resume添加窗口时崩溃。
- debug、灰度环境下,通过反射获取
- 覆盖所有窗口添加场景排查泄漏点
- 排查自定义Toast逻辑:Android 12以上系统Toast默认走窗口添加路径,如果应用自己实现了全局Toast队列、或者频繁触发Toast没做去重/队列上限限制,极端场景下会残留大量未回收的Toast窗口。
- 排查所有手动调用
WindowManager.addView()的场景:包括全局悬浮球、新功能引导浮层、WebView视频小窗、输入法自定义浮层、第三方SDK的广告/红包/提示浮层,不少第三方SDK的浮层关闭逻辑存在缺陷,页面退出时没有调用removeView(),反复进出对应页面就会残留大量无主窗口。 - 补全PopupWindow的生命周期绑定:不要只在点击关闭事件里调用PopupWindow的dismiss方法,必须在宿主Activity/Fragment的
onDestroy()生命周期里强制dismiss当前页面持有的所有PopupWindow,否则宿主销毁后PopupWindow的窗口不会被系统自动回收,反复进出页面就会持续累积泄漏的窗口。
- 提效复现技巧
- debug环境下可以直接通过反射把
WindowManagerGlobal的窗口计数上限从300改成10,正常走核心业务路径、页面跳转、弹窗/浮层操作,只要存在泄漏点很快就会触发崩溃,比等自然累积到300个窗口效率高很多。 - 对存在弹窗、浮层的页面做反复进出的压力测试,同时观察全局窗口计数,如果退出页面回到首页后窗口计数没有回落,说明对应页面存在窗口泄漏。
- debug环境下可以直接通过反射把
临时兜底方案
可以在应用初始化时hookWindowManagerGlobal的addView方法,调用前先检查当前窗口总数,如果已经超过280,先遍历清理所有已经脱离宿主、处于不可见状态的无效窗口,再执行原有addView逻辑,避免线上直接崩溃。
内容的提问来源于stack exchange,提问作者RH201
相关产品推荐
相关产品推荐

