在移动互联网,连接是比较重要的传播媒质,但不少时候咱们又但愿用户可以回到APP中,这就要求APP能够经过浏览器或在微信中被方便地唤起。javascript
这是一个既直观又很好的用户体验,但在实现过程当中会遇到各类问题:java
如何解决未安装APP时的作好引导页web
如何在微信中唤醒APP浏览器
在iOS9中如何处理universal link被用户误关的状况微信
如何解决Android各类机型、各类第三方浏览器致使的兼容问题等app
在APP未安装状况下,引导用户下载后打开APP后,如何进入以前唤起时指定的页面或内容,即如何实现场景还原code
在微信中唤醒APP时,如何进入指定的页面或内容blog
下面是我一些我的的经验分享。接口
这块内容其实比较简单,在网上都有不少资料可供查阅,就再也不赘述。事件
首先须要说明,无论iOS仍是Android,浏览器都不可能预知本地是否安装了某个APP的。或者更严谨地说,咱们不能经过浏览器来预知本地是否安装。由于就算浏览器能够读取本地应用的安装列表,可是目前也没任何一家浏览器提供查询的API,因此这条路是走不通的。
本质上浏览器是经过URL scheme打开APP,一个APP能够设置一个或多个打开本身的URL scheme。好比,Twitter就注册本身能被「twitter://」打开。
其实,若是是作APP间相互跳转是比较简单的。iOS就可使用 UIApplication 的 canOpenUrl 方法来检测URL scheme 是否能打开对应的APP。好比,若是「twitter://」检测能被打开,也就说明本地安装了 Twitter 。再用 UIApplication 的 openURL 方法,就能打开Twitter了。Android 中的作法相似。
由于iOS9和以前的iOS系统有区别,因此这里咱们也要区别对待。
iOS中默认经过Safari打开URL scheme,方法通常以下两种:
直跳方式:点击连接、修改 window.location 等。
iframe 方式:在 body 上添加 iframe,设置src属性为跳转的URL scheme。
第一种状况:
<a href="schemeUrl">唤醒你的APP</a>
或者
window.location.href = schemeUrl;
但在第一种状况,若是APP唤醒失败,或者APP未安装的话,不少时候都会跳到错误页,这很影响用户体验,而咱们的要求多是跳转到其余页面或者下载APP。
后一种方法不会引发页面可见的变化(例如页面内容变成一个新页面),不会致使浏览器历史记录的变化,大体实现以下:
<a href="APP下载地址">下载或打开APP</a> <script> $('a').click(function() { var ifr = document.createElement('iframe'); ifr.src = '自定义 URL scheme'; ifr.style.display = 'none'; document.body.appendChild(ifr); setTimeout(function(){ document.body.removeChild(ifr); }, 3000); }); </script>
过程是这样:点击 a 标签时,首先会尝试打开URL scheme,若是成功,就唤起APP;若是失败,则跳转到 href 属性,即下载页。
但这个方案在不少安卓机型上有问题,为保证可用,改用第一种方案:
$('a').click(function() { location.href = '自定义 URL scheme'; t = Date.now(); setTimeout(function(){ if (Date.now() - t < 1200) { location.href = 'Android 下载地址'; } }, 1000); return false; }
理想过程是这样:浏览器尝试打开 URL scheme,在1秒计时后,检查当前时间,若是实际时间已过 1200 毫秒,说明唤起APP 成功(唤起 APP 会让浏览器的定时器变慢);若是没超过 1200 毫秒,极可能是没有安装应用,就跳到下载地址。
或者换种方式:
var ifr = document.createElement('iframe'); ifr.src = 'com.baidu.tieba://'; ifr.style.display = 'none'; document.body.appendChild(ifr); var openTime = +new Date(); window.setTimeout(function(){ document.body.removeChild(ifr); if( (+new Date()) - openTime > 2500 ){ window.location = 'http://exam.com/xxxx.apk'; } },2000)
但原理都是同样,利用setTimeout。但这其实不稳定,由于Android是基于Linux的分时多任务的,setTimeout的基准误差可能会没那么大。
但若是设置比较小的运行间隔(<30ms),在浏览器或者webview中,应用切换到后台,setInterval
会被很明显的延迟执行,好比设置一个运行间隔20ms,总计运行100次的定时器,若是页面一直处于前台,则100次跑完,总耗时与 100x20=2000ms不会有太大差别,但页面在后台运行时,此时间会明显超过2000ms。能够利用这一点来实现是否成功打开APP检测及回调。
function openApp(openUrl, appUrl, action, callback) { //检查app是否打开 function checkOpen(cb){ var _clickTime = +(new Date()); function check(elsTime) { if ( elsTime > 3000 || document.hidden || document.webkitHidden) { cb(1); } else { cb(0); } } //启动间隔20ms运行的定时器,并检测累计消耗时间是否超过3000ms,超过则结束 var _count = 0, intHandle; intHandle = setInterval(function(){ _count++; var elsTime = +(new Date()) - _clickTime; if (_count>=100 || elsTime > 3000 ) { clearInterval(intHandle); check(elsTime); } }, 20); } //在iframe 中打开APP var ifr = document.createElement('iframe'); ifr.src = openUrl; ifr.style.display = 'none'; if (callback) { checkOpen(function(opened){ callback && callback(opened); }); } document.body.appendChild(ifr); setTimeout(function() { document.body.removeChild(ifr); }, 2000); }
另外,能够经过 document.hidden
或 document.[webkit|moz|ms]Hidden
来判断页面是否被置入后台(即应用被唤起),或visibilitychange
事件,但对于Android 4.4版本一下则不支持。
在 iOS 9 上,iframe 方案变得不可用。按不能使用以前Android的代码,由于在打开自定义 URL scheme 时,会弹出对话框,询问是否用 xx 应用来打开。每每用户还没来得及点击打开,定时器又触发了,致使跳到 App Store。
能够在尝试打开URL scheme 后,再加一个页面跳转,这样对话框会被覆盖,再刷新页面,就能无需确认唤起APP:
$('a').click(function() { location.href = '自定义 URL scheme'; location.href = '下载页'; location.reload(); }
这里,下载页延时 2 秒跳转到 App Store。
APP已安装这是没问题的,但若是APP未安装,跳 App Store 的请求会失败。这时可使用两个定时器:
$('a').click(function() { location.href = '自定义 URL scheme'; setTimeout(function() { location.href = '下载页'; }, 250); setTimeout(function() { location.reload(); }, 1000); }
不过在iOS9中实际上是支持universal link的,就是一个http域名形式,在微信中均可以唤起APP。若是未安装的话,能够直接引导用户去APP store下载。
能够参考这篇文章
http://www.magicwindow.cn/doc...
主要是在安卓上,总归会有各类兼容问题,知乎的解决办法是,提供两个按钮,一个下载,一个打开APP,让用户本身选。
由于微信将唤起本地APP的接口给禁了,因此微信中是不能直接唤起APP的,通常作法是提示用户在浏览器中打开,以后的流程仍是咱们上面讲的内容。
可是,在iOS9中,这个限制是能够突破的,也就是说能够直接唤起APP。方法就是使用咱们上文提到的universal link。
在Android和iOS8及其如下系统中,咱们能够利用腾讯的亲儿子:应用宝。简单讲,就是把你的唤起地址配置成你APP的应用宝地址,微信中跳转到这个地址后,若是用户已经安装了APP,则可直接唤起,若是没有安装,则可直接点击下载,以下图示:
但这里有坑须要注意。
对于使用universal link来讲,以下图所示用户在微信中打开APP以后,可能不当心点击右上角的连接(比方说点几分享,却不当心点击了"mlinks.cc"),致使跳到外部浏览器中,以下图所示:
这时候再在微信中就打不开APP了,由于universal link已被关闭,这是iOS9的机制,无法改变,这时候用户再在微信中打开,就得须要一个中间页来引导用户在外部浏览器中打开APP,以下图所示:
另外,在微信中唤醒APP默认只能到达首页,即不能到达指定页面或内容,若是想要作,则须要额外的处理。
从以上内容能够总结出:要作一个兼容性很好的方案,就须要考虑各类状况,在不一样的状况适配不一样的方案,比方说用户是在手机浏览器打开仍是微信中打开,或者是在pc中打开,universal link是否被关闭等,这就使代码实现变得复杂,且容易出错,且还有安卓平台机型众多、浏览器众多等致使的兼容问题。
若是以为实现难度或者成本过高,你能够考虑使用魔窗的mLink。只要你加了魔窗的sdk,就能够经过相似“https://s.mlinks.cc/AA01”的连接,在任何环境下打开你的APP(若是在pc机上打开,浏览器中将会出现APP下载地址的二维码),上面提到的问题都不复存在,而且魔窗已经兼容超过600台以上安卓机型的第三方主流浏览器。并且关键的是,不论是在手机浏览器中,仍是在微信中打开,你能够指定唤起APP后直达APP中的某个页面或内容(某个促销商品等),就算用户没安装APP,点击下载安装以后,再打开,仍是跳转到指定的页面,这就是场景还原,或者叫作Deffered Deep Linking。