移动互联网时代,“用户增长”成为每个公司关注的重点话题。为了将更多用户引导到客户端内,产品经理会习惯性地在网页的各个地方巧妙隐藏唤醒App的“机关”。
常见的出现场景
浏览器 —唤醒—> App
用户在浏览器中浏览网页时,当检测到该网页来自于某个App时,此时可以引导用户呼起或者下载App
微信、QQ —唤醒—> App
用户将App中自己喜欢的内容分享到微信、QQ,在站外打开网页时,可以正常浏览,也可以引导用户呼起或者下载App
接下来,让我们深入研究下唤醒App的几种解决方案?
唤醒App的几种解决方案
1、 URL Scheme 方式
- 条件
- APP需要注册自己的URL Scheme,用来唯一标识一个App。
- Scheme格式:
<scheme域名>://<path>?<params>=<value>
- 代码
1) iframe方式
var _iframe = document.createElement('iframe');
_iframe.src = scheme;
_iframe.style.display = 'none';
document.body.appendChild(_iframe);
window.setTimeout(function(){
document.body.removeChild(_iframe);
if((+new Date()) - openTime > 2500) {
window.location.href = url;
}
}, 2000);
2)a链接方式
<a href="<scheme域名>://<path>?<params>=<value>">打开APP</a>
3)location.href 直接跳转
window.location.href = "<scheme域名>://<path>?<params>=<value>"
-
优缺点
- 优点:iOS、Android均支持,开发简单;web-native协议制定简单。
- 缺点:
由于要考虑用户没有安装App的情况,所以当用户没有安装时,通过延迟会跳转到AppStore。iOS9+当跳转App时,会弹出一个弹框,让用户选择是否跳转,此时还在当前页,setTimeout中的代码会继续执行,导致用户还没来得及选择,就已经跳到AppStore。
若用户未安装App,Android上scheme打开失败,没有任何提示,延迟之后,跳下载页。但是iOS9+会先弹出个万恶的跳转失败的弹窗,延迟之后,再跳下载页。iOS9+呼端确认弹窗
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:呼端失败跳下载页。
- 适用于安装就跳转,不安装就跳下载的呼端场景
- 参考文档(有关客户端配置scheme)
Android scheme呼起App
iOS 客户端URL Scheme配置以及使用
2、 Android Intent 方式
- 条件
A little known feature in Android lets you launch apps directly from a web page via an Android Intent. One scenario is launching an app when the user lands on a page, which you can achieve by embedding an iframe in the page with a custom URI-scheme set as the
src
, as follows:<iframe src="paulsawesomeapp://page1"> </iframe>
. This works in the Chrome for Android browser, version 18 and earlier. It also works in the Android browser, of course. - 构造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>
- 支持情况
- iOS:不支持
- Android:Android的Intent方式比URL Scheme方式要靠谱,能通过参数设置呼端失败要跳转的地址;若手机能匹配到对应App,则成功呼到对应页面,若未安装,会跳手机自带的应用市场下载。
- 参考文档
Android Intents with Chrome
3、Universal Links
-
条件
- iOS9+系统
- Universal Link是通过标准的http/https协议链接唤起App;若未安装App,访问此通用链接,可以自定义页面;
-
优缺点
- 优点
唯一性:不像自定义的Scheme方式,该方式是通过标准的http/https协议的链接到你的App.未安装App时,自定义的Scheme方式无法打开,而http/https的Universal Link通用链接有很好的兼容性;
安全性:当用户安装了App时,iOS会从网站上去下载说明文件(下面所说的apple-app-site-association的文件,说明这个App可以打开哪些http链接),这个说明文件是只有开发者有权限写并上传到网站的根目录,所以App与网页http链接的关联是安全的。
可变性:当用户没有安装App时,Universal Link链接也能正常打开,也可以设置:未安装App时,可以用Safari打开该链接。
简单性:一个URL链接,可以将网站和App做关联。
私有性:其他App在不知道你是否安装的情况下,也可以和你的App进行相互通信(因为只是一个普通的http链接啊)。 -
缺点:只支持 iOS9+系统;在开启了Universal Link之后,用户可以通过该方式呼端,成功呼端后右上角会显示链接地址(iOS9、 iOS10会出现面包屑导航,iOS11+没有面包屑导航(如下图)),当用户一不小心手动点击这个地址时(俗称:Universal link误关),Universal Link会失效(失效后,Universal Link呼端就永远失败了,永远呼不起来,跳下载页,所以我们要重新激活Universal Link)需要引导用户到Safari浏览器,引导用户点击Safari内置顶部的系统呼端条,重新激活Universal Link,也可以将链接复制到备忘录中,长按链接出现用App打开,也可激活Universal Link。
- 优点
-
开启Universal Links开关
- 注册一个域名并支持https
- 有权限上传到网站根目录.well-know(这个权限是为了上传一个Apple指定的文件apple-app-site-association)
- 创建一个JSON文件名字为apple-app-site-association的文件(文件名必须是这个!!!)
// apple-app-site-association文件配置
{
"applinks": {
"apps": [],
"details": {
"ZVC23L5QY4.com.domain.app": {
"paths": ["*"]
}
}
}
}
注意:
大小写敏感;严格匹配配置的path路径;apple-app-site-association文件的读取,只在第一次启动App时加载;iOS9.2开始,在相同的domain下,Universal Link不work,必须跨域;
以上只是重点部分内容,详细说明请参考:
打通 iOS 9 的通用链接(Universal Links)
了解了呼端的几种方式,分享下我们团队完整的呼端解决方案
完整的解决方案
由于我们无法判断用户是否安装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);
}
}
注意:
前面提到的URL Scheme 方式的iframe和a链接方式,需要考虑用户未安装客户端情况:延迟跳转到下载页。这个延长时间的设置很关键!!!延长时间的设定需要考虑:如果延长时间小于App的启动时间,App还未启动,就执行setTimeout代码;如果延长时间较长,当用户未安装App时,需要等待特别久的时间才能执行setTimeout代码。
对代码封装,根据不同业务呼端需求,可以提炼几个可配参数:呼端scheme地址、呼端失败是否跳下载页、下载页链接、呼起客户端失败超时时间、呼起回调;这样做的好处是:调用组件时,根据不同需求传递参数即可。
优化
- 实际测试时发现:当成功呼起App时,用户再次返回到Safari浏览器的页面时已经跳转到下载页面了,此时需要对setTimeout做清除定时器处理。
- 当本地App被唤起时,App处于设备可视窗口的最高层,此时浏览器进入后台程序页面会被隐藏掉,会触发pagehide与visibilitychange事件,此时应先清除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);
代码中有关事件具体参考:Page Visibility API文档
测试结果
测试机型主要针对iOS和Android默认浏览器、Chrome等一些常用主流浏览器,iOS9+微信内嵌请尝试使用 Universal Link 方式呼起客户端。
测试反馈
以上完整的呼端解决方案我们完美的使用了很久,直到2018年1月7日,微信封掉了内置浏览器里的Universal Link呼端,导致微信内引导呼端的流程彻底中断!!!
考虑到微信是站外分享很重要的渠道,我们当然不能!!!放过!!!
- 首先:我们有一个思路:既然微信容器内不能使用Universal Link呼端,那么我们可以脱离微信容器,做一个中转页面,引导用户去浏览器呼端。
-
其次:想想用户之前习惯性操作都是一次性直接点击呼端成功的,引导的话链路有点长,用户难免会吐槽。
所以,针对中转页面iOS客户端新增 Universal Link 匹配规则(以中转页面链接为主),以便在微信内,「使用浏览器打开」后,被系统拦截后可以直接打开客户端,不用引导去浏览器。
当然,对于Android用户,我们考虑到也可以引导用户去应用宝呼端。 - 最后做了一点小优化:在浏览器的中转页面中,采用双保险呼端。当客户端配置好Universal Link链接时,Safari浏览器访问中转页面,页面顶部会出现系统的呼端条,引导用户下拉,用户可以选择使用系统呼端条打开,从而也能激活Universal Link,防止误关;采用按钮点击尝试呼端(关闭Universal Link方式)。
-
针对Universal Link误关的现象,可以采纳这两种方法
- 用样式的方式遮挡右上角面包屑导航。可以使得iOS9、iOS10和iOS11+达到视觉的一致性。但是样式控制不好,用户体验会很不好。
- 获取面包屑导航的触发事件,可以手动的控制触发后跳转的地址(可以引导跳转到中转页面,引导用别的方式呼端)。这种算hack的解决方案。要考虑成本问题,是否采纳。
中转页面
iOS客户端采用Universal link进行系统拦截
双保险呼端
至此,站外呼端方案完美落幕~~~