移动端唤起APP的尝试

最近接到一个任务做一个在浏览器上唤起APP的插件,今天就写写在实现过程中的一些心得。其实就是针对浏览器各种hack。

使用场景

希望通过微信、QQ、短信或者浏览器引导用户进入APP。功能上期望可以直接通过中间页唤起APP,亦或者业务方直接引用js代码,控制唤起APP。并且需要监控唤起APP成功或失败的动作。

现状分析

进过一段时间的尝试,发现目前使用最多的几种方式是:Scheme、Universal Links以及Safari的APP提示广告条。

其中Scheme的形式Adr和IOS都是支持的,成本也是最小的,只需要APP开发时注册了scheme。虽然scheme的方式算是一个比较好的方案,但是他的问题页很严重,可能导致页面跳转到错误页、IOS safari上讨厌的提示框等等。

下面对比下不同方式支持的情况(相关名词可以google):

浏览器\方式 scheme Universal Links safari广告 Android intent
IOS 支持 支持 支持
Adr 支持 支持
微信\QQ 不支持 IOS支持 不支持 Adr 支持
Adr chrome 支持 支持
IOS safari 支持 支持 支持

对比分析,我们可以总结出下面几种情况:

  • 微信\QQ 只有腾讯全家桶可以跳转,或者加入微信的白名单,比如携程
  • Android Chrome在25(某篇文章说是这个版本,没测试)版本后使用scheme的形式已经不能在唤起APP了,只能通过Android intent方式打开
  • IOS safari 用scheme 浏览器会有提示,如果没装APP,直接提示无效网页,影响体验
  • Universal Links 可以在任意APP里面跳转,但需客户端支持,要求略高,不过在IOS上是比较完美的解决方案
  • safari广告 点击自行查看介绍 ,感觉不太适合来作为唤起APP的主要方式
  • 排除上面几种情况之后,基本上可以通过iframe、a标签或者location.href愉快的玩耍了

代码实现

获取浏览器信息

首先需要的就是判断浏览器类型,区分出安卓、IOS以及微信等运行环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function _getUAInfo(customerUA) {
var clientUA = customerUA || navigator.userAgent,
isIOS = (/iPhone|iPad|iPod/i).test(clientUA),
isAndroid = (/Android/i).test(clientUA),
baseInfo = {
clientUA: clientUA,
isIOS: isIOS,
isAndroid: isAndroid,
isIOSSafari: isIOS && (/Safari/i).test(clientUA) && !(/Chrome/i).test(clientUA),
isWX: (/micromessenger/i).test(clientUA),
//TODO 安卓的chrome 判断需要优化一下
isAndroidChrome: isAndroid && ((/Chrome\/([\d.]+)/).test(clientUA) || (/CriOS\/([\d.]+)/).test(clientUA)) && !(/UCBrowser|XiaoMi|Browser/i).test(clientUA)
};
// APP协议头
baseInfo.protocal = 'xxxx';
// app scheme地址
baseInfo.fullSchemeUrl = 'apprpotocal://scheme';
// IOS上的Universal Link实现(需客户端实现)
baseInfo.iosUniversalLink = 'xxx';
// 安卓intent (需客户端实现)
baseInfo.androidChromeUrl = 'xxx';
}

尝试打开APP

有了上一步的结果,打开APP就比较容易了。

其中Universal Link只能人为点击触发,不能通过js click触发跳转,所以通常需要用一个引导页引导用户点击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function _openApp() {
var AppOpt = _getUAInfo();
// iframe 和 a 标签打开方式
var iframe = document.createElement("iframe"),
aLink = document.createElement("a"),
body = document.body,
hidenStyle = "display:none;width:0px;height:0px;";
// 隐藏iframe及a
iframe.style.cssText = aLink.style.cssText = hidenStyle;
if (new RegExp(AppOpt.protocal, 'gi').test(AppOpt.clientUA)) {
// 已经在客户端中,直接跳转到scheme地址
window.location.href = AppOpt.fullSchemeUrl;
} else if (AppOpt.isWX) {
// 微信由于禁止了scheme跳转 解决方案有以下几种情况
// 1. 分别用Universal Link 和 intent 能够跳过微信的限制直接唤起APP
// 2. 跳转到应用宝下载页
// 3. 接入微信sdk
} else if (AppOpt.isAndroidChrome) {
// adr chrome 用更高级的intent
aLink.href = AppOpt.androidChromeUrl;
body.appendChild(aLink);
aLink.click();
} else if (AppOpt.isIOS) {
// 考虑到Universal Link不能通过js触发 iOS上其它浏览器可以通过a链接实现
// safari上会有一个确认的警告框,这个没办法去掉,可以考虑使用Universal Link
aLink.href = AppOpt.fullSchemeUrl;
body.appendChild(aLink);
aLink.click();
} else {
// 其它浏览器就用iframe吧
body.appendChild(iframe);
iframe.src = AppOpt.fullSchemeUrl;
}
// 处理超时
}

超时处理

前面也提到了,我们期望引导用户在APP里面访问。所以在我们的使用场景当中,往往是通过一系列活动页进行推广,如果用户真的没有安装app,我们页希望能够它能够看到touch的活动页面。

怎么实现呢?那就是使用超时,比如在引导页5S后自动会跳转到touch地址。

1
2
3
4
5
function timeoutHandle() {
setTimeout(function () {
window.location.href = 'http://touch.xxx.xxx';
},5000);
}

日志和监控

有上面几个步骤之后,我们差不多已经达到目的了。但是对于我们开发者来说,并没有明确的知道是不是唤起了APP,用户在使用的场景可能是个黑洞,即使页面崩溃掉了,我们都毫不知情。

有没有好的办法监控是否成功唤起APP?答案是没有。(因为浏览器没有提供任何是否预装某APP的api、也没有直接唤起APP的函数)

那我们就一点儿办法都没了?肯定不是的,我们可以通过visibilitychange、pageshow、pagehide以及安卓浏览器在后台运行时setTimeout的延迟会很长等表现间接判断是否唤起了APP,并记录日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function resultHandle() {
// 标签是否可见
document.addEventListener('visibilitychange', function() {
sendLog(document.hidden ? '页面隐藏,可能唤起了APP' : '页面显示出来了,可能回到了浏览器');
}, false);
document[type]('webkitvisibilitychange', function() {
}, false);
// pagehide 必须绑定到window
window.onpagehide = function () {}
window.onpageshow = function () {}
// blur focus 事件也可以
}
function sendLog() {
}

不过遗憾的时,上面的几个事件不是所有浏览浏览器都支持,并不能精准的判断。

而且如果用户在打开页面的时候回到了桌面或者电话呼入也有可能被误判为唤起APP。所以前面的超时时长就很重要,一般可以设置成3000毫秒。

毕竟时间接判断,能够记录大部分成功失败的量也可以有助于分析问题。

总结

以上,是对唤起APP的一些尝试,没有完美的方案,只能是尽可能hold 住各种情况。

最后在立一个flag,等有时间了把唤起APP公司内部使用的插件弄一个公开版的。

参考资料就不贴了,我也不知道为了实现这个插件,经历了些什么。像Universal Linkintent这些方案可以自行google。