前言
开工大吉,祝大家2019诸事顺利! 当我们用微信或者浏览器打开一个html5页面的时候,有时候会有浮层,提示要不要打开相关APP,当然也有的会自动唤醒到app中。这种唤醒是如何做到的呢?
今天简单的写下浏览器是如何唤醒APP的。当然,关于唤醒,涉及的东西蛮多,例如地址加参数唤醒,设置唤醒白名单,唤醒黑名单等等。h5地址和Scheme地址的转换等等。本文简单写下唤醒的相关原理。
如下图:
点击浮层,出现如下提示:
请问这是如何做到的呢?
常见唤醒App方式
一、 Scheme 方式
APP需要注册自己的URL Scheme,用来唯一标识一个App。
Scheme格式:
:// ? =
1、iframe方式
var _iframe = document.createElement('iframe');
_iframe.src = scheme;
_iframe.style.display = 'none';
document.body.appendChild(_iframe);
2、a链接方式
<a href="<scheme域名>://<path>?<params>=<value>">打开APP</a>
3、location.href 直接跳转
window.location.href = "<scheme域名>://<path>?<params>=<value>"
问题:
由于我们没有办法判断用户手机里面安装了APP没有,因此,需要做一个延迟跳转功能。所以当用户没有安装时,通过延迟会跳转到AppStore。iOS9+当跳转App时,会弹出一个弹框,让用户选择是否跳转,此时还在当前页,setTimeout中的代码会继续执行,导致用户还没来得及选择,就已经跳到AppStore。 若用户未安装App,Android上scheme打开失败,没有任何提示,延迟之后,跳下载页。但是iOS9+会先弹出个万恶的跳转失败的弹窗,延迟之后,再跳下载页。
支持情况
URL Scheme方式一直被广泛使用,但是有些App并不认可,比如:微信、手机百度;站在这些App的角度上考虑,他们并不希望用户为了看更多分享内容,跳出自己的App,因此他们就在客户端内拦截了scheme方式呼端,导致URL Scheme方式在微信、手机百度中彻底失效!!!当然微信是存在一个白名单的,对于白名单中的分享链接是不会屏蔽scheme调用的。 安卓App厂商差异很大,情况比较多样化(比如:Android Chrome版本25+通过iframe方式呼端失败 )
兼容性
Android系统:Chrome for Android无法通过iframe方式来调用scheme,而通过a链接的方式可以成功调用,而针对Chrome内核的浏览器如360浏览器,对于iframe和a链接的方式都能支持,所以对Chrome内核的浏览器采用a链接的方式来调用scheme;对于其他浏览器,如UC,QQ浏览器则采用iframe方式调用scheme。 iOS系统:Safari浏览器不支持 iframe可直接做页面跳转;对于UC、Chrome、QQ只能通过a链接方式调用scheme。 上述提到的屏蔽scheme方式的App:呼端失败跳下载页。
二、 Android Intent 方式
在安卓手机的chrome浏览器或者安卓手机浏览器上面,可以intent方式唤醒
intent:
HOST/URI-path
#Intent;
package=[string]; // android app包名
action=[string];
category=[string];
component=[string];
scheme=xxxx; // 协议头
S.browser_fallback_url=[url] // 可选,scheme启动客户端失败时的跳转页,一般为下载页,需编码
end;
<!--Intent方式呼端-->
<a href="intent://<role>/<path>#Intent;scheme=<scheme>;package=com.domain;S.browser_fallback_url=[url];end">打开APP</a>
三、Universal Links方式唤醒
首先要开启Universal Links开关
步骤如下:
1、注册一个域名并支持https
2、有权限上传到网站根目录.well-known(这个权限是为了上传一个Apple指定的文件apple-app-site-association)
3、创建一个JSON文件名字为apple-app-site-association的文件(文件名必须是这个!!!)
// apple-app-site-association文件配置
{
"applinks": {
"apps": [],
"details": {
"ZVC23L5QY4.com.domain.app": {
"paths": ["*"]
}
}
}
}
微博的配置地址:https://m.weibo.cn/.well-known/apple-app-site-association
携程的配置地址:https://m.ctrip.com/.well-known/apple-app-site-association
当然,淘宝,京东等等都有。
完整唤醒方案
由于我们无法判断用户是否安装App,所以以上所有的方案都只能尝试呼端。整合上述这些方案,具体思路如下:
// iOS9+在开启Universal Link开关的前提下,优先使用Universal Links方式呼端
if (!closeUnilink && isios && iosVer && iosVer >= 9) {
// 通过Universal Links方式获取通用呼端链接
const unilink = getUnilink(opts.scheme, opts.unilink);
if (unilink) {
window.location.href = unilink;
}
} else {
// 不支持Intent方式,采用 URL Scheme 方式
if (!canIntent) {
// iOS9+ Safari 不支持iframe方式
if (!isWv && (ua.indexOf('safari') > -1 && iosVer && iosVer >= 9)) {
link(scheme);
} else {
iframe(scheme);
}
// 处理未安装客户端情况:延迟跳转到下载页
setTimeout(function () {
gotoDownload();
},延长时间);
} else {
// Android支持Intent方式时: intent呼起客户端
intent(scheme);
}
}
tips:
前面提到的URL Scheme 方式的iframe和a链接方式,需要考虑用户未安装客户端情况:延迟跳转到下载页。这个延长时间的设置很关键!!!延长时间的设定需要考虑:如果延长时间小于App的启动时间,App还未启动,就执行setTimeout代码;如果延长时间较长,当用户未安装App时,需要等待特别久的时间才能执行setTimeout代码。
对代码封装,根据不同业务呼端需求,可以提炼几个可配参数:呼端scheme地址、呼端失败是否跳下载页、下载页链接、呼起客户端失败超时时间、呼起回调;这样做的好处是:调用组件时,根据不同需求传递参数即可。
优化方案
实际测试时发现:当成功呼起App时,用户再次返回到Safari浏览器的页面时已经跳转到下载页面了,此时需要对setTimeout做清除定时器处理。 当本地App被唤起时,App处于设备可视窗口的最高层,此时浏览器进入后台程序页面会被隐藏掉,会触发pagehide与visibilitychange事件,(关于这两个API,不了解的同学可以查看我之前文章:https://www.haorooms.com/post/page_api_cst )。 此时应先清除setTimeout事件;同时,document.hide属性设置为true,所以setTimeout内不做跳转处理,防止页面跳转到下载页面。
实际开发中,为了防止某些浏览器不支持这个 Page Visibility API,最好同时监听pagehide事件,这样会比较保险(相关代码如下)。
// 页面隐藏时触发
window.onpagehide = function () {
if (timeout) {
clearTimeout(timeout);
}
};
// 页面的可见状态变化时,会触发
visibilitychange = function () {
const tag = document.hidden || document.webkitHidden;
if (tag && timeout) {
clearTimeout(timeout);
}
}
document.addEventListener('visibilitychange', visibilitychange, false);
// 兼容多的浏览器事件
document.addEventListener('webkitvisibilitychange', visibilitychange, false);
关于唤醒更多文章,我后期会补充一些。简单的唤醒方式就先写到这里。