症状

同一套导航页面,两种访问方式结果完全不同:

访问方式页面结构学校桥 window.android
navi.6767.chat/React SPA,导航页被 <iframe src="navi/index.html"> 嵌入 iframe❌ iframe 里 window.android 不存在
navi.6767.chat/navinavi/index.html 就是主 frame✅ 直接可用,定位秒成

用户一直从 SPA 主入口进(/),导航页在 iframe 里拿不到桥、走 postMessage 代理链路又失败,一直以为「学校桥拿不到」;偶然直接访问 /navi,导航页变主 frame,桥直接可用,定位成功。

触发条件

  • 第三方 Android 壳(addJavascriptInterface 注入 window.android)内嵌本 Web 应用;
  • 本应用是 SPA + iframe 架构:功能页(导航)嵌在主壳页面的 <iframe> 里;
  • 功能页依赖 WebView 注入的 JS 桥。

根因

Android WebView 的 addJavascriptInterface 只把 JS 对象注入主 frame(WebView 加载的顶层文档)。 iframe 子文档拥有独立的 JS 全局作用域,拿不到注入到主 frame 的 window.android。这是 WebView 的固有限制,不是页面代码问题。

因此同一个 URL 的 navi/index.html:

  • 被 iframe 嵌入时 → 子 frame,window.android === undefined;
  • 直接访问时 → 主 frame,window.android 存在且可调。

要让 iframe 内页面用上桥,只能主 frame 代调后 postMessage 回传(本项目 App.jsx 的 navi-request-school-bridge 代理),而代理链路多一跳、依赖主 frame React 壳版本(还叠加了 CDN 缓存旧壳的风险),远不如让功能页直接做主 frame 稳。

解决

让依赖 JS 桥的功能页直接做主 frame,不要嵌 iframe。 本项目的 navi.6767.chat/navi 就是直接访问,桥可用。SPA 主入口里用 iframe 嵌入导航页属于过度嵌套,对 WebView 桥接场景是自找麻烦。

如果必须 iframe 嵌入,只能走「主 frame 代调 + postMessage 回传」代理,并保证:

  1. 主 frame 壳是最新版(含代理分支)——注意 CDN/WebView 缓存可能给旧壳;
  2. 代理有超时与兜底(本项目 app.js 的 2.5s watchdog 回落直接调宿主 frame 桥)。

下次怎么识别

  • 「手机上 APP 里不行 / 浏览器里行」先问一句:页面是主 frame 还是 iframe?
  • 功能页依赖的 native 桥在 iframe 里测不到 → 先查是否被 <iframe> 嵌入,再查是不是直接访问该 URL 就能用;
  • 诊断:直接访问功能页 URL(绕开 SPA)测桥,能通则问题锁定在 iframe 嵌套层。

相关概念


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Programming