唤起APP时遇到的坑以及解决思路。
需求描述:webView唤起手机APP,如果有就唤起,如果没有就跳到下载页。
1.如何唤起APP?
URL Scheme
通过URL Scheme方式唤起APP的主流方法有两种iframe唤起和location.href。
- iframe
let ifr = document.createElement('iframe');
let scheme = 'weixin://';
ifr.src = scheme;
ifr.style.display = 'none';
document.body.appendChild(ifr);
兼容性较差, 在 ios 9+ 上诸如 safari、UC、QQ浏览器中,以及安卓 chrome 25+ 版本,均无法唤起 APP。
- location.href
window.location.href = 'weixin://';
兼容性相比iframe好很多
2.如何得知APP是否成功唤起?
浏览器实际上没有能力判断手机里是否安装了某个App,也无法判断APP是否被唤起。 但是APP被成功唤起时,H5被切换至后台,换言之只要我们能够监听到页面被切换至后台这个动作,就可以认为APP被成功唤起。
visibilitychange
visibilitychangeH5新增API,可以监听页面被切换操作,当浏览器的某个标签页切换到后台,或从后台切换到前台时就会触发该事件。
window.addEventListener('visibilitychange', () => {
if(document.visibilityState === 'hidden'){
alert('页面已被切换至后台');
};
})
visibilityState作用是记录当前标签页在浏览器中的激活状态。当document.visibilityState的值为hidden时,即页面被切换至后台。
但是在IOS中为了节省资源,浏览器切换到后台或者锁屏时JS会被挂起visibilitychange事件无效。
setTimeout
如果app被唤醒浏览器进入后台JS被挂起,JS虽然不执行,但计时器会被推迟,那么用户从进入APP到切换回浏览器通常会超过1s,如果我们检测到计时器的执行被推迟超过1s,即可认为APP成功唤起导致浏览器进入后台;
let start = new Date().getTime();
setTimeout(()=>{
if((new Date().getTime()) - start > 3000){
alert('success');
}else {
alert('failed');
};
},2000);
完整代码如下:
export function callApp(schema,timeOut) {
let timer,start = new Date().getTime();
let myPromise = new Promise((resolve, reject)=>{
window.location.href = schema;
window.addEventListener('visibilitychange', () => {
if(document.visibilityState === 'hidden'){
clearTimeout(timer);
resolve();
};
});
timer = setTimeout(()=>{
let end = new Date().getTime();
if(end - start > timeOut + 1000){
clearTimeout(timer);
resolve();
}else {
reject();
};
},timeOut);
})
return myPromise
}
......
callApp('weixin://').then(()=>{
alert('success');
})
.catch(()=>{
alert('failed');
})
在规定的时间内(timeOut),倘若未检测到visibilitychange事件或计时器延时触发,则断定APP唤起失败(reject)