注册
web

H5唤醒APP技术方案入门级介绍

内容大纲


image.png




什么是H5唤醒App


“唤醒 App”指的是:


🐔🏀 从「另一个应用 / 系统环境」跳转并打开「你本地已安装的 App」


唤醒 App = 跨应用启动



典型来源端(“从哪来”)



  • 🐔 浏览器(Safari / Chrome / 系统浏览器)
  • 🏀 微信 / QQ / 钉钉 / 支付宝
  • 🐔 其他第三方 App
  • 🏀 短信 / 邮件
  • 🐔󠁧󠁢󠁥󠁮󠁧󠁿 推送通知
  • 🎤 二维码



目标端(“到哪去”)



  • 🐉 你已经安装在手机里的原生 App
  • 并且:
  • 启动 App
  • 还能跳到 指定页面





唤醒 App 的技术方案


deep link

在讲具体的技术选型方案之前


我们先要说什么是 deep link(唤端技术的本质)




deep link 本质上不是“打开 App” ,而是“让操作系统把一次跳转请求路由给某个 App 处理



  • 浏览器 / 微信 / 系统 并不是“主动打开 App”
  • 而是 把一个“链接”交给系统
  • 系统再决定:

    • 1.有没有 App 能处理?
    • 2.交给谁?
    • 3.怎么交?



所以 deep link 是系统能力,不是 JS 技巧。




为什么会有这么多种唤醒方案?


  • 1.iOS 和 Android 的系统模型不同
  • 2.安全策略不同
  • 3.浏览器、微信等容器又各自加了一层限制

于是结果就是:



“同一个目标(打开 App),在不同系统上只能用不同的入口”





这也是为什么你看到的主流方案是这三类



  • 1.URL Scheme(最原始)
  • 2.Universal Link(iOS 官方)
  • 3.App Link / Chrome Intents(Android 官方)



方案1.URL Scheme

在关于H5混合开发的通信中,我们就已经介绍了URL Scheme是JS bridge通信方式的一种


它的使用场景并不局限于“唤醒 App”,而是更广义的:





👉 通过一个特定格式的 URL,让系统或原生拦截并执行对应逻辑



一个典型的 URL Scheme 长这样:


myapp://page/detail?id=123

其中:



  • myapp:协议名(Scheme)
  • page/detail:业务路径
  • id=123:参数

对浏览器来说,它并不关心这个 URL 是否“合法”, 它唯一做的事是:把这个 URL 交给操作系统处理。


Scheme 方案唤醒app能生效的前提是:App 必须提前向系统注册这个协议名




在 App 安装阶段



  • iOS / Android 会在系统层记录
  • “某个 App 能够处理哪些 Scheme

系统会维护一张映射关系:


Scheme(协议名) → App

一旦这个映射存在,系统就具备了“路由能力”。




当系统再次遇到相同 Scheme 的 URL 时,流程会变成



URL → 操作系统 → 查找注册关系 → 启动对应 App → 传递参数



整个过程发生在 系统层面,与 H5 是否运行在 WebView、是否使用 JS Bridge 本身并没有直接关系。


Safari → App 为例


Safari 点击链接

系统识别这是 Universal Link / Scheme

系统查找有没有 App 声明能处理

有 → 启动 App(cold / warm)

把参数交给 App




H5侧实现

① 通过 window.location.href 跳转

这是最直接、最直观的一种方式:


window.location.href = 'zhihu://'

它的行为非常明确:



  • 1.当前页面发起一次 URL 跳转
  • 2.浏览器发现这是一个非 http(s) 协议
  • 3.将该 URL 交给操作系统处理

早期移动浏览器系统浏览器中,这种方式成功率较高,也是最常见的实现。


但它的问题也很明显:



  • 1.会破坏当前页面状态
  • 2.在强管控容器(如微信)中通常会被直接拦截
  • 3.无法判断 App 是否已安装



② 通过隐藏 iframe 触发跳转

这种方式曾经被广泛用于 “无刷新唤醒” 的场景:


const iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = 'zhihu://'
document.body.appendChild(iframe)

其原理是:



  • 1.利用 iframe 加载资源的行为
  • 2.间接触发 Scheme
  • 3.避免页面发生整体跳转

在一段时间内,这种方式被认为是:



location.href 更“温和”的唤醒方式



但随着浏览器和容器安全策略的收紧:



  • iframe 加载非标准协议被限制
  • 微信、QQ 等环境几乎完全失效

目前这类方式更多只存在于历史代码或兼容逻辑中




③ 通过 <a> 标签跳转

这是最“标准 HTML”的方式:


<a href="zhihu://">打开知乎 App</a>

它的特点是:



  • 1.依赖用户真实点击
  • 2.符合浏览器的交互安全模型
  • 3.成功率通常高于自动跳转

在部分环境中:



“用户点击触发” 本身就是是否允许唤醒的重要判断条件



因此,<a> 标签在某些浏览器中的表现,反而比 JS 自动跳转更稳定。




④ 通过 JS Bridge 由原生侧发起

在 App 内 WebView 场景下,最稳定的方式其实是:


window.miduBridge.call('openAppByRouter', {
url: 'zhihu://'
})

这种方式的本质是:



  • 1.H5 并不直接触发 Scheme
  • 2.而是通过 JS Bridge 通知原生
  • 3.由 原生代码主动发起跳转



这也是 混合开发中最推荐的做法,因为:



  • 1.不受浏览器安全策略影响
  • 2.成功率最高
  • 3.可完全由 App 控制兜底逻辑




实际开发问题

在实际开发中,一个非常现实的问题是:



H5 发起 Scheme 跳转后,如何判断 App 是否真的被成功唤起?





但是事实上是对于 URL Scheme 这种系统级跳转机制 来说:



前端并不存在一个“可靠、官方、100% 准确”的判断方式



这是由 Scheme 的实现机制本身决定的。


为什么前端无法直接判断?

当 H5 触发 Scheme 跳转后:



  • 1.浏览器将 URL 交给操作系统
  • 2.系统尝试查找是否存在可处理该 Scheme 的 App
  • 3.如果存在,则直接拉起 App



这个过程发生在:



浏览器 → 操作系统 → App



而 H5 所处的位置是:



浏览器沙箱内



浏览器不会告诉 H5:



  • 1.是否找到了 App
  • 2.是否成功启动
  • 3.是否被系统或容器拦截

因此,H5 无法拿到任何明确的成功 / 失败回调




目前的主流方案是【推测】

方式一:页面可见性变化(最常用)


let hidden = false

document.addEventListener('visibilitychange', () => {
if (document.hidden) {
hidden = true
}
})

setTimeout(() => {
if (!hidden) {
// 大概率唤起失败
}
}, 1500)


原理是:



  • 1.App 被拉起时
  • 2.浏览器页面会进入后台
  • 3.触发 visibilitychange

如果页面始终未进入隐藏状态,大概率唤醒失败


! 注意:

这是“概率判断”,不是绝对结论。




方式二:定时器兜底跳转


location.href = 'zhihu://'

setTimeout(() => {
location.href = 'https://appstore.xxx.com'
}, 2000)

逻辑是:



  • 1.尝试唤醒 App
  • 2.如果 2 秒内页面未被中断
  • 3.认为 App 未安装或唤醒失败
  • 4.自动跳转下载页

这是最常见的商业实现方式。\





以上方法均不可靠


因为它们都依赖于一个前提:



“App 被唤起,一定会导致页面进入后台”



但现实中:



  • 系统弹窗
  • 权限确认
  • 容器拦截
  • 多任务切换

都会导致误判。


所以结论非常明确:



Scheme 的唤醒结果,只能“推测”,不能“确认”





不过第 ④ 种方式,其实是一个例外。


window.miduBridge.call('openAppByRouter', { url: 'zhihu://' })


因为这一步是:



由原生主动发起跳转



所以:



  • 原生知道自己是否成功处理了跳转
  • 可以通过 JS Bridge 回调结果给 H5

window.miduBridge.call(
'openAppByRouter',
{ url: 'zhihu://' },
(result) => {
if (result.success) {
// 唤起成功
} else {
// 唤起失败
}
}
)



  • 纯 H5 + Scheme

    • 无法准确判断唤醒是否成功
    • 只能通过行为推测


  • JS Bridge + 原生发起

    • 可以获得明确结果
    • 成功率与可控性最高



也正是这个差异,导致了今天的现实:


Scheme 更适合作为“兜底工具”,而不是主方案




scheme方案的其他缺点

除了前面提到的 安全性差、用户体验不佳、无法准确判断唤起结果 外,URL Scheme 还有几个现实工程中必须考虑的缺点:




① 协议名可能被重复注册或占用



  • 1.URL Scheme 依赖的是 协议名(如 myapp:// 来标识 App
  • 2.系统层面并没有强制保证唯一性
  • 3.如果不同 App 注册了相同协议名:

    • 用户点击 Scheme 时,系统可能唤醒错误的 App
    • 导致业务逻辑混乱,甚至产生安全隐患





② 部分 App 或容器主动屏蔽



  • 微信、QQ、支付宝等强管控容器对 Scheme 跳转有严格限制
  • 常见表现:

    • 1.自动跳转失效
    • 2.iframe / location.href 被直接拦截
    • 3.用户点击 <a> 标签也可能无法唤醒


  • 原因:

    • 1.防止恶意跳转、劫持安装流
    • 2.控制容器内的用户体验




换句话说,即便你的协议名注册正确,Scheme 在这些环境下往往失效





③ 无统一管理和安全约束



  • 1.URL Scheme 本身没有域名验证或证书绑定机制
  • 2.任何 App 都可以注册
  • 3.没有办法验证调用者或跳转来源
  • 4.容易被用作“恶意唤醒”或劫持入口

  •   <br>





方案2.Universal Link / App Link

随着 URL Scheme 的局限性暴露出来:



  • 1.协议名可能冲突
  • 2.容器或浏览器屏蔽
  • 3.无法安全验证来源



Apple 和 Google 分别提出了官方解决方案



  • iOS → Universal Link
  • Android → App Link / Chrome Intents

它们的核心理念很一致:



通过 HTTPS 链接 + 系统校验,让 App 唤醒更安全、更可靠





2.1 Universal Link(iOS)



Universal Link 是 iOS 9 之后新增的功能,它允许开发者 直接通过 HTTPS 链接唤醒 App


相比 URL Scheme,它有几个明显优势:



  1. 自然降级:如果 App 没有安装,点击链接会直接打开网页,无需前端判断唤起是否成功。
  2. 用户体验更好:不会弹出“是否打开 App”的确认框,唤端效率更高。
  3. 安全可靠:链接必须绑定到 App 的域名,避免协议名冲突或被劫持。



核心原理


Universal Link 的实现原理可以概括为两步:



  1. 1.App 注册域名

    • 在 iOS 项目中,需要声明 App 支持的域名
    • 系统通过这个绑定来识别哪些链接可以交给 App 处理。


  2. 2.域名配置 apple-app-site-association 文件

    • 在对应域名的根目录下放置 apple-app-site-association 文件,声明 App 支持哪些路径。
    • 当用户点击该域名的链接时,iOS 会检查该文件,并判断 App 是否可以处理。
    • 如果 App 安装了,就直接唤起;否则,打开网页






对前端同学来说,不需要关注文件的具体配置,只需与 iOS 同学确认好支持的域名即可。






  • 系统在点击链接时,会偷偷做三件事:

    1. 1.验证域名是否和 App 绑定(Apple 服务器文件 + App 配置)
    2. 2.检查 App 是否已安装
    3. 3.匹配 App 内路由,如果符合则直接唤起 App 指定页面


  • 未安装 App,则自然打开网页页面,不会报错或失效

  •   <br>



相对于 URL Scheme,Universal Link 的优势非常明显:



  1. 1.无弹窗提示

    • 唤端时不会弹出“是否打开 App”的确认框
    • 用户体验更顺畅,可以减少用户流失


  2. 2.自然降级能力

    • 无需关心用户是否安装 App
    • 对于未安装 App 的用户,点击链接会直接打开对应网页
    • 这也解决了 URL Scheme 无法准确判断唤端失败的问题


  3. 3.平台限制

    • Universal Link 目前只能在 iOS 系统使用
    • Android 需要使用 App Link 或 Chrome Intents


  4. 4.用户触发要求

    • 必须由用户主动点击触发
    • 自动跳转、iframe 触发等方式无法保证唤起成功







H5侧代码


在 H5 页面中,触发 Universal Link 非常简单,就像普通的网页链接一样


function openByUniversal() {
// 打开知乎问题页
window.location.href = 'https://oia.zhihu.com/questions/64966868';
}

或者使用 <a> 标签:


<a href="https://oia.zhihu.com/questions/64966868">打开 App</a>

特点:



  • 1.与普通网页跳转一致,前端不需要做额外判断
  • 2.如果 App 安装了,系统会直接拉起 App 并跳转到对应页面
  • 3.如果 App 未安装,则打开网页,兜底自然


🔹 对前端同学来说,Universal Link 的操作非常简单,不需要关心底层配置,只需确认域名和路径由 iOS 同学支持即可。





⚠️ 但是它在 iOS 容器中仍然有限制:



  • 微信、QQ 等仍然可能拦截
  • 因为容器本身不允许把链接交给系统



2.2 App Link / Chrome Intents(Android)

Android 的解决方案和 iOS 类似,但实现上更“开放”:



  • 1.App Link:和 Universal Link 一样,通过 HTTPS + 域名校验来保证安全
  • 2.Chrome Intents:允许开发者直接指定 包名 + Scheme + 路由,用于兜底或精确跳转

示例:


https://www.example.com/product/123

或者使用 Intent:


intent://product/123#Intent;scheme=myapp;package=com.example.app;end


  • 系统会检查 App 是否安装
  • 安装则唤起指定页面
  • 未安装则跳转应用商店



H5 侧触发方式


①通过普通 HTTPS 链接触发 App Link


function openByAppLink() {
// 打开商品详情页
window.location.href = 'https://www.example.com/product/123';
}

或者直接用 <a> 标签:


<a href="https://www.example.com/product/123">打开 App</a>

原理:



  • 1.系统检测链接对应域名是否绑定 App
  • 2.App 安装了 → 唤起并跳转指定页面
  • 3.App 未安装 → 自动打开网页,兜底自然



② 通过 Intent URL 触发 Chrome Intents


function openByIntent() {
window.location.href = 'intent://product/123#Intent;scheme=myapp;package=com.example.app;end';
}

特点:



  • 1.可以指定 App 包名和 Scheme
  • 2.App 安装 → 唤起指定页面
  • 3.App 未安装 → 跳转应用商店,确保用户可获取 App



2.3 相比 Scheme 的优势

优势说明
安全域名验证避免被劫持或重复注册
成功率高系统直接控制唤醒流程
可自然降级App 未安装时自动跳网页或应用商店
用户体验好不弹确认框,跳转顺畅



2.4 需要注意的点


  • 1.Universal Link / App Link 仍然会被部分 容器拦截 (尤其是微信)
  • 2.域名和 App 的绑定必须在 服务端 + App 配置 同步
  • 3.Android 上不同浏览器行为可能略有差异,需要在测试时覆盖主流浏览器



方案3:微信环境下的唤醒方案

微信环境下的 H5 唤醒 App,和普通浏览器相比有几个显著特点



  1. 1.绝大部分 Scheme 被拦截

    • 无论是 location.href、iframe 还是 <a> 标签
    • 微信会直接阻止跳转,防止外部 App 劫持


  2. 2.Universal Link / App Link 成功率有限

    • iOS 的 Universal Link 在微信里也可能被拦截
    • Android 的 App Link / Chrome Intents 在微信内同样可能无效




🔹 也就是说,在微信环境下,“传统唤端方案”几乎失效。





3.1可行方案

① 通过 跳转到 App Store / 应用商店


  • 对于未安装 App 的用户,是最安全、最通用的兜底方案
  • 缺点:用户必须手动下载,体验不如直接唤端

window.location.href = 'https://apps.apple.com/cn/app/idxxxxxx';



② 使用 中转页 / 提示页


  • 先打开一个中转 H5 页面(WebView 或浏览器打开),提示用户点击按钮唤醒 App
  • 按钮可以触发 Scheme 或 Universal Link
  • 优势:

    • 1.提示用户手动操作,提高唤醒成功率
    • 2.可以结合埋点统计唤醒行为


  • 缺点:

    • 额外增加一个页面,增加跳转成本



H5侧


<!-- 中转提示页 -->
<button id="openAppBtn">打开 App</button>

<script>
document.getElementById('openAppBtn').addEventListener('click', function() {
// 方式 1:使用 URL Scheme(兜底方案)
window.location.href = 'myapp://page/detail?id=123';

// 方式 2:使用 Universal Link(iOS)
// window.location.href = 'https://www.example.com/page/detail?id=123';

// 可选:2 秒后兜底到应用商店
setTimeout(() => {
window.location.href = 'https://apps.apple.com/cn/app/idxxxxxx'; // iOS 应用商店
// 或 Android 下载链接
}, 2000);
});
</script>



特点:



  • 1.必须用户点击才能触发
  • 2.可以结合 setTimeout 兜底下载
  • 3.可以在按钮点击时触发埋点统计唤醒成功率



③ 小程序或企业号协作


  • 对于企业内部或自家 App:

    • 可以通过 小程序 / 企业微信接口 调起 App
    • 优点:成功率高,可控
    • 缺点:仅限特定生态





H5 侧示例(假设使用企业微信 JS-SDK)


<button id="openAppBtn">打开 App</button>

<script>
// 假设已经引入企业微信 JS-SDK 并完成 config
document.getElementById('openAppBtn').addEventListener('click', function() {
if (window.wx && wx.invoke) {
wx.invoke('openEnterpriseChat', { // 示例接口
useridlist: 'user_id',
chatType: 1
}, function(res) {
if(res.err_msg == "openEnterpriseChat:ok") {
console.log('App 唤起成功');
} else {
console.log('唤起失败,兜底逻辑');
window.location.href = 'https://apps.apple.com/cn/app/idxxxxxx';
}
});
}
});
</script>




特点:



  • 1.成功率高,原生接口可明确回调
  • 2.适合企业内部 / 自家生态
  • 3.不适用于普通微信用户



④ 微信开放标签 <wx-open-launch-app>(Android)

微信为了改善 Android H5 唤醒体验,提供了 开放标签 wx-open-launch-app,可以让前端 H5 直接在微信里唤醒 App


使用示例


<wx-open-launch-app
appid="wx123" <!-- 你注册的 App ID -->
extinfo="page=home&id=123"> <!-- 透传参数,可在 App 内使用 -->

<script type="text/wxtag-template">
<button>打开 App</button>
</script>

</wx-open-launch-app>

原理:



  • 1.标签本身是微信官方提供的组件
  • 2.内部会调用 微信客户端唤醒 App 的能力
  • 3.可以透传参数给 App,直接跳到指定页面



⚠️ 使用前提



  1. 1.微信认证

    • 公众号或小程序必须经过微信认证


  2. 2.App 在白名单内

    • 需要申请微信开放能力并配置白名单
    • 只有在白名单内的 App 才能被唤醒


  3. 3.仅限微信环境

    • 该标签在普通浏览器或非微信环境下无法使用



特点



  • 1.成功率高:比传统 Scheme / Universal Link 在微信中稳定
  • 2.前端简单:不需要写 JS 复杂逻辑,只需包一层标签即可
  • 3.可透传参数:可直接带参数跳到指定页面

限制



  • 1.仅适用于 Android
  • 2.必须满足认证 + 白名单条件
  • 3.仅能在微信内使用



⑤微信环境下 iOS 唤醒:Universal Link

微信中,前面提到的 URL Schemeiframe 等方式几乎都被拦截,无法自动唤起 App。


iOS 唯一可行且推荐的方案是 Universal Link:



  • 1.用户点击 H5 页面里的 HTTPS 链接
  • 2.iOS 系统检查该域名是否绑定了 App
  • 3.App 已安装 → 直接唤起并跳转指定页面
  • 4.App 未安装 → 打开网页,自然兜底

H5 触发方式


<a href="https://oia.zhihu.com/questions/64966868">打开 App</a>

<script>
function openByUniversal() {
window.location.href = 'https://oia.zhihu.com/questions/64966868';
}
</script>



特点:



  1. 1.成功率最高

    • iOS 系统直接判断是否唤起 App
    • 不受微信容器拦截 Scheme 的影响


  2. 2.用户体验好

    • 不弹出“是否打开 App”的确认框
    • 点击即可直接唤起 App


  3. 3.自然降级

    • App 未安装时,自动打开网页
    • 前端无需额外逻辑判断唤端成功与否



注意:



  • 1.仅适用于 iOS 微信
  • 2.Android 微信仍需中转页或 <wx-open-launch-app> 等方案
  • 3.必须事先和 iOS 同学确认支持的域名和 Universal Link 配置

作者:pinkQQx
来源:juejin.cn/post/7594087108594237503

0 个评论

要回复文章请先登录注册