内容大纲

什么是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 长这样:
bash
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 为例
js
Safari 点击链接
↓
系统识别这是 Universal Link / Scheme
↓
系统查找有没有 App 声明能处理
↓
有 → 启动 App(cold / warm)
↓
把参数交给 App
H5侧实现
① 通过 window.location.href 跳转
这是最直接、最直观的一种方式:
js
window.location.href = 'zhihu://'
它的行为非常明确:
- 1.当前页面发起一次 URL 跳转
- 2.浏览器发现这是一个非 http(s) 协议
- 3.将该 URL 交给操作系统处理
在早期移动浏览器 和系统浏览器中,这种方式成功率较高,也是最常见的实现。
但它的问题也很明显:
- 1.会破坏当前页面状态
- 2.在强管控容器(如微信)中通常会被直接拦截
- 3.无法判断 App 是否已安装
② 通过隐藏 iframe 触发跳转
这种方式曾经被广泛用于 "无刷新唤醒" 的场景:
js
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"的方式:
js
<a href="zhihu://">打开知乎 App</a>
它的特点是:
- 1.依赖用户真实点击
- 2.符合浏览器的交互安全模型
- 3.成功率通常高于自动跳转
在部分环境中:
"用户点击触发" 本身就是是否允许唤醒的重要判断条件
因此,<a> 标签在某些浏览器中的表现,反而比 JS 自动跳转更稳定。
④ 通过 JS Bridge 由原生侧发起
在 App 内 WebView 场景下,最稳定的方式其实是:
js
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 无法拿到任何明确的成功 / 失败回调。
目前的主流方案是【推测】
方式一:页面可见性变化(最常用)
js
let hidden = false
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
hidden = true
}
})
setTimeout(() => {
if (!hidden) {
// 大概率唤起失败
}
}, 1500)
原理是:
- 1.App 被拉起时
- 2.浏览器页面会进入后台
- 3.触发
visibilitychange
如果页面始终未进入隐藏状态,大概率唤醒失败
! 注意:
这是"概率判断",不是绝对结论。
方式二:定时器兜底跳转
ini
location.href = 'zhihu://'
setTimeout(() => {
location.href = 'https://appstore.xxx.com'
}, 2000)
逻辑是:
- 1.尝试唤醒 App
- 2.如果 2 秒内页面未被中断
- 3.认为 App 未安装或唤醒失败
- 4.自动跳转下载页
这是最常见的商业实现方式。\
以上方法均不可靠
因为它们都依赖于一个前提:
"App 被唤起,一定会导致页面进入后台"
但现实中:
- 系统弹窗
- 权限确认
- 容器拦截
- 多任务切换
都会导致误判。
所以结论非常明确:
Scheme 的唤醒结果,只能"推测",不能"确认"
不过第 ④ 种方式,其实是一个例外。
js
window.miduBridge.call('openAppByRouter', { url: 'zhihu://' })
因为这一步是:
由原生主动发起跳转
所以:
- 原生知道自己是否成功处理了跳转
- 可以通过 JS Bridge 回调结果给 H5
js
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.容易被用作"恶意唤醒"或劫持入口
xml
<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,它有几个明显优势:
- 自然降级:如果 App 没有安装,点击链接会直接打开网页,无需前端判断唤起是否成功。
- 用户体验更好:不会弹出"是否打开 App"的确认框,唤端效率更高。
- 安全可靠:链接必须绑定到 App 的域名,避免协议名冲突或被劫持。
核心原理
Universal Link 的实现原理可以概括为两步:
-
1.App 注册域名
- 在 iOS 项目中,需要声明 App 支持的域名。
- 系统通过这个绑定来识别哪些链接可以交给 App 处理。
-
2.域名配置 apple-app-site-association 文件
- 在对应域名的根目录下放置 apple-app-site-association 文件,声明 App 支持哪些路径。
- 当用户点击该域名的链接时,iOS 会检查该文件,并判断 App 是否可以处理。
- 如果 App 安装了,就直接唤起;否则,打开网页。
对前端同学来说,不需要关注文件的具体配置,只需与 iOS 同学确认好支持的域名即可。
-
系统在点击链接时,会偷偷做三件事:
- 1.验证域名是否和 App 绑定(Apple 服务器文件 + App 配置)
- 2.检查 App 是否已安装
- 3.匹配 App 内路由,如果符合则直接唤起 App 指定页面
-
未安装 App,则自然打开网页页面,不会报错或失效
xml
<br>
相对于 URL Scheme,Universal Link 的优势非常明显:
-
1.无弹窗提示
- 唤端时不会弹出"是否打开 App"的确认框
- 用户体验更顺畅,可以减少用户流失
-
2.自然降级能力
- 无需关心用户是否安装 App
- 对于未安装 App 的用户,点击链接会直接打开对应网页
- 这也解决了 URL Scheme 无法准确判断唤端失败的问题
-
3.平台限制
- Universal Link 目前只能在 iOS 系统使用
- Android 需要使用 App Link 或 Chrome Intents
-
4.用户触发要求
- 必须由用户主动点击触发
- 自动跳转、iframe 触发等方式无法保证唤起成功
H5侧代码
在 H5 页面中,触发 Universal Link 非常简单,就像普通的网页链接一样
js
function openByUniversal() {
// 打开知乎问题页
window.location.href = 'https://oia.zhihu.com/questions/64966868';
}
或者使用 <a> 标签:
js
<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 + 路由,用于兜底或精确跳转
示例:
arduino
https://www.example.com/product/123
或者使用 Intent:
bash
intent://product/123#Intent;scheme=myapp;package=com.example.app;end
- 系统会检查 App 是否安装
- 安装则唤起指定页面
- 未安装则跳转应用商店
H5 侧触发方式
①通过普通 HTTPS 链接触发 App Link
js
function openByAppLink() {
// 打开商品详情页
window.location.href = 'https://www.example.com/product/123';
}
或者直接用 <a> 标签:
js
<a href="https://www.example.com/product/123">打开 App</a>
原理:
- 1.系统检测链接对应域名是否绑定 App
- 2.App 安装了 → 唤起并跳转指定页面
- 3.App 未安装 → 自动打开网页,兜底自然
② 通过 Intent URL 触发 Chrome Intents
js
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.绝大部分 Scheme 被拦截
- 无论是
location.href、iframe 还是<a>标签 - 微信会直接阻止跳转,防止外部 App 劫持
- 无论是
-
2.Universal Link / App Link 成功率有限
- iOS 的 Universal Link 在微信里也可能被拦截
- Android 的 App Link / Chrome Intents 在微信内同样可能无效
🔹 也就是说,在微信环境下,"传统唤端方案"几乎失效。
3.1可行方案
① 通过 跳转到 App Store / 应用商店
- 对于未安装 App 的用户,是最安全、最通用的兜底方案
- 缺点:用户必须手动下载,体验不如直接唤端
js
window.location.href = 'https://apps.apple.com/cn/app/idxxxxxx';
② 使用 中转页 / 提示页
-
先打开一个中转 H5 页面(WebView 或浏览器打开),提示用户点击按钮唤醒 App
-
按钮可以触发 Scheme 或 Universal Link
-
优势:
- 1.提示用户手动操作,提高唤醒成功率
- 2.可以结合埋点统计唤醒行为
-
缺点:
- 额外增加一个页面,增加跳转成本
H5侧
js
<!-- 中转提示页 -->
<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)
js
<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。
使用示例
js
<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.微信认证
- 公众号或小程序必须经过微信认证
-
2.App 在白名单内
- 需要申请微信开放能力并配置白名单
- 只有在白名单内的 App 才能被唤醒
-
3.仅限微信环境
- 该标签在普通浏览器或非微信环境下无法使用
特点
- 1.成功率高:比传统 Scheme / Universal Link 在微信中稳定
- 2.前端简单:不需要写 JS 复杂逻辑,只需包一层标签即可
- 3.可透传参数:可直接带参数跳到指定页面
限制
- 1.仅适用于 Android
- 2.必须满足认证 + 白名单条件
- 3.仅能在微信内使用
⑤微信环境下 iOS 唤醒:Universal Link
微信中,前面提到的 URL Scheme 、iframe 等方式几乎都被拦截,无法自动唤起 App。
iOS 唯一可行且推荐的方案是 Universal Link:
- 1.用户点击 H5 页面里的 HTTPS 链接
- 2.iOS 系统检查该域名是否绑定了 App
- 3.App 已安装 → 直接唤起并跳转指定页面
- 4.App 未安装 → 打开网页,自然兜底
H5 触发方式
js
<a href="https://oia.zhihu.com/questions/64966868">打开 App</a>
<script>
function openByUniversal() {
window.location.href = 'https://oia.zhihu.com/questions/64966868';
}
</script>
特点:
-
1.成功率最高
- iOS 系统直接判断是否唤起 App
- 不受微信容器拦截 Scheme 的影响
-
2.用户体验好
- 不弹出"是否打开 App"的确认框
- 点击即可直接唤起 App
-
3.自然降级
- App 未安装时,自动打开网页
- 前端无需额外逻辑判断唤端成功与否
注意:
- 1.仅适用于 iOS 微信
- 2.Android 微信 仍需中转页或
<wx-open-launch-app>等方案 - 3.必须事先和 iOS 同学确认支持的域名和 Universal Link 配置