在开放网页上,点一下 Universal Link,系统就有机会打开你的 App;Android 上的 App Link 同理。但在 WebView 里,这次跳转会死掉。WebView 自己加载 URL 时,操作系统根本不会被问到「该由哪个 App 处理」,于是你发布的关联文件(apple-app-site-association / assetlinks.json)永远不会被查阅。对金融 App 来说,这意味着本该把用户推进银行 App 做 3DS / SCA 强客户认证、或推进钱包 App 确认付款的重定向,会悄悄落回网页——甚至什么地方都不去。宿主自有 runtime 桥接原生打开能力,让跳转在每个引擎上都生效。

Universal Link 在 WebView 里从不触发

Universal Link 与 Android App Link 是操作系统级机制。用户在 Safari、Chrome 或系统浏览器里点链接时,iOS / Android 会检查该域名的关联文件,若已安装匹配 App 就直接打开它。这一拦截发生在任何浏览器渲染页面之前。但当 WebView 加载同一个 URL 时,宿主 App 已经开着一个网页视图,直接把它渲染在内部——系统从不被询问,关联文件形同虚设,App 永远不会启动。Apple 文档明确写道:用 WKWebView 处理 Universal Link 会让 iOS 在 Safari 打开网站而非你的 App;只有在已嵌入的视图里由用户点按,链接才可能打开 App,且仍需宿主配合。JavaScript 重定向(window.location.href)不被算作用户主动导航,也会被系统解析器忽略。

自定义 URI Scheme 同样被压制

旧的兜底方案——像 myapp:// 这样的自定义 URI Scheme——在 WebView 里同样不可靠。iOS 上,页面在 WKWebView 内部没有任何受支持的方式强制宿主交出控制权;自定义 Scheme 在多数 WebView 场景被压制,失败且无任何回退。Android 上,裸 WebView 同样不解析 App Link,只有 Android Custom Tabs(真正的浏览器实例)才会。宿主可以在 Android 覆写 shouldOverrideUrlLoading、或在 iOS 实现 decidePolicyFor navigationAction 去调用 UIApplication.open,但大多数 App——尤其是包裹第三方内容的社交与超级 App——并不会这么做,因为把用户留在网页里才是它们的诉求。结果到处一样:页面依赖的跨 App 跳转不会发生。

跳转失败时什么会断

  • 3DS / SCA 跳银行 App 失败。 强客户认证常常把手伸给发卡行的 App。在 WebView 里这一跳被吞掉,认证卡住或悄悄回到银行从未完成的网页兜底。
  • 钱包与付款确认断链。 「去钱包 App 确认」的链接本应深链进 Google Pay、Apple Pay 或银行 App,结果毫无反应。
  • 账号绑定与 OAuth 回调失灵。 「打开验证器 / 银行 App 审批」的流转到空白或死胡同页面。
  • 归因与安装承接丢失。 本应跨过安装、把用户送到正确页面的延迟深链断裂,损害激活漏斗。

runtime 的解法

宿主自有 runtime 接管跨 App 跳转。页面只调一个统一 API——runtime.openApp(url)——宿主通过平台原生机制解析:iOS 用 UIApplication.open(配合 Universal Link 或经宿主审校的 Scheme),Android 用 Intent / App Link,并在没有安装目标 App 时给出网页回退。runtime 还能识别自己正处于嵌入式视图,主动暴露「在浏览器打开」的明确入口,而不是一个看似点了没反应的链接。一次集成,每个引擎,一个真正落地的跳转。

维度WebView宿主自有 runtime
系统链接解析从不被询问宿主调原生打开
Universal / App Link被吞经原生打开生效
自定义 URI Scheme被压制 / 静默失败经宿主审校交接
iOS 出口无可靠方式UIApplication.open
Android 出口仅 Custom TabsIntent / App Link
3DS / 钱包跳转断通
无目标 App 时无明确网页回退

原生 App 能在每台设备上打开另一个 App;裸 WebView 几乎不能。runtime 给页面一个调用——以及一个真正落地的跳转。

参考来源