问题简述
uni-app Android 平台,App 按 Home 键退后台,再切回前台后,后续通过 uni.navigateTo 打开的新页面会出现幽灵页面:页面生命周期正常执行、页面栈入栈成功、plus.webview.isVisible() 返回 true,但页面无画面渲染,触摸事件穿透到底层上一页,点击操作作用于下层页面。
根因定位:uni Android 每个页面为独立原生子窗口(AbsoluteLayout → WebParentView → WebView)。为加速跳转 runtime 会预创建 INVISIBLE+0x0 空窗口;真正打开页面时,需要设置 VISIBLE + 设置全屏尺寸。App 后台恢复 (onResume) 场景下,预创建的页面窗口仅标记为 VISIBLE,遗漏设置 LayoutParams 全屏宽高,生成VISIBLE + 0×0的原生容器。
额外诱因:App onShow 阶段频繁非幂等修改 Window Flag / 导航栏颜色,会触发 Activity 全局 requestLayout,会显著提高该 bug 复现概率;将系统栏修改逻辑改为幂等(仅值变更才修改),可降低复现概率,但无法彻底根除。
复现环境
- 平台:Android(测试机型:xxx,Android 版本:xxx)
- uni-app 版本:xxx
- 打包方式:App 离线打包 / 云端打包(runtime 版本:xxx)
- 项目类型:uni-app(非 x)
复现步骤
- 打开 App,正常进入首页 / 我的页面;
- 点击 Home 键,App 退至后台,停留数秒;
- 从系统任务栏切回 App(触发 onResume);
- 执行
uni.navigateTo跳转新页面:- 第一次跳转:使用后台恢复之前已经预创建好的窗口,页面正常展示;
- 第二次及之后跳转:使用后台恢复后新预创建的原生窗口,触发幽灵页问题。
现象表现
- JS 层:
onLoad/onShow正常触发,页面栈正常入栈,接口请求正常执行;plus.webview.getWebviewById获取 webview 对象,isVisible()返回 true; - 原生视图树(hierarchyviewer/LayoutInspector 抓取对比)
✅ 正常页面:
AbsoluteLayout VISIBLE 1440x3200
└─ WebParentView 1440x3200
└─ WebView内核 1440x3200
❌ 幽灵异常页面:
AbsoluteLayout VISIBLE 0x0
└─ WebParentView 0x0
└─ WebView内核 0x0
- 视觉:屏幕仍然显示下层旧页面,新页面无任何渲染画面;
- 交互:点击事件穿透 0 尺寸视图,全部命中下层页面,无法操作新页面;
- 无效尝试:调用 webview.show ()、render:"always"、主动 requestLayout 均无法修复;仅等待下一次 App 前后台切换 onResume 时 runtime 会重新统一计算窗口尺寸,幽灵页面才会恢复正常。
根因分析
uni-app Android 运行时页面机制:每个页面是挂载在根容器下独立 WindowManager 子 View。navigateTo 为优化打开速度,提前预创建页面容器,初始状态 INVISIBLE + 0x0(此为正常预加载逻辑)。
页面正式打开,runtime 需要执行两步:
- 将 AbsoluteLayout 可见性设置为 VISIBLE
- 给原生 View 设置全屏宽高 LayoutParams
Bug 触发逻辑:App 后台恢复 onResume 流程中,runtime 新建预加载页面,在触发 navigateTo 时,只执行了可见性修改,遗漏设置全屏尺寸,形成 VISIBLE、宽高为 0 的原生容器。
0 尺寸 VISIBLE 视图特性:
- 可见性标记为 VISIBLE,JS API 读取可见性为 true;
- 原生 View 宽高 0,不会绘制像素,不会接收触摸事件,事件穿透下层。
诱发条件补充:
在 App onShow 生命周期内,反复调用 addFlags/clearFlags/setNavigationBarColor 修改窗口属性,会触发 Activity 全局 requestLayout。在 onResume 敏感时机,全局重布局会干扰 runtime 内部页面窗口尺寸恢复逻辑,提高 bug 出现概率;将系统栏相关修改改为幂等执行(仅参数发生变化时才修改 Window 属性),可降低复现概率,但不能完全杜绝该底层缺陷。
临时兜底方案(JS + plus.android 反射修复)
仅作为上层兜底补丁,非根治方案,期待 runtime 原生修复
- 视图检测工具:通过
plus.android反射遍历页面视图树,识别VISIBLE && (width==0 || height==0)的页面容器;过滤 INVISIBLE 的正常预加载窗口; - 自动修复:检测栈顶 webview 为 0 尺寸幽灵页时,手动调用
webview.setStyle强制赋值全屏宽高(top:0,left:0, 屏幕宽高),补齐 runtime 缺失的布局参数; - 时序:页面 onShow/onReady 延时 500ms 执行检测;navigateTo 入栈 300ms 执行检测;
- 优化系统栏代码:syncSystemBarColors 改为幂等,Window Flags、导航栏颜色、setSystemUiVisibility 仅在值变更时调用,避免 onShow 反复触发全局窗口重布局。
期望
- 修复 Android runtime,App 后台恢复 onResume 场景下,预创建页面打开时,保证同时设置 VISIBLE 和全屏 LayoutParams,杜绝 VISIBLE+0x0 幽灵 WebView 容器;
- 优化 onResume 阶段多页面窗口尺寸计算逻辑,避免全局窗口布局干扰子页面尺寸初始化;
- 文档 / 更新日志补充该场景修复说明。
0 个评论
要回复文章请先登录或注册