在做 h5 页面中,会遇到这样一个需求,有一个立即打开的按钮,如果本地安装了我们的 app,那么点击就直接唤起本地 app,如果没有安装,则跳转到下载。
首先想到的是两个问题:一是如何唤起本地 app,二是如何判断浏览器是否安装了对应 app。
如何唤起本地 app
首先,想要实现这个需求,肯定是必须要客户端同学的配合才行,因此我们不用知道所有的实现细节,我们从前端角度思考看这个问题,需要知道的一点是,ios 与 Android 都支持一种叫做 schema 协议的链接。比如网易新闻客户端的协议为
JavaScript
- newsapp://xxxxx
当然,这个协议不需要我们前端去实现,我们只需要将协议放在 a 标签的 href 属性里,或者使用 location.href 与 iframe 来实现激活这个链接。而 location.href 与 iframe 是解决这个需求的关键。
在 ios 中,还支持通过smart app banner来唤起 app,即通过一个 meta 标签,在标签里带上 app 的信息,和打开后的行为,代码形如
XHTML
- <meta name="apple-itunes-app" content="app-id=1023600494, app-argument=tigerbrokersusstock://com.tigerbrokers.usstock/post?postId=7125" />
需要注意的是:我们就没办法通过这个协议在微信中直接唤起 app。原因是微信里屏蔽了 schema 协议,除非你是微信的合作伙伴之类的,他们专门给你配置进白名单。
因此我们会判断页面场景是否在微信中,如果在微信中,则会提示用户在浏览器中打开。
如何判断本地是否安装了 app
首先我们可以确认的是,在浏览器中无法明确的判断本地是否安装了 app。因此我们必须采取一些取巧的思路来解决这个问题。
我们能够很容易想到,采用设置一个延迟定时器 setTimeout 的方式,第一时间尝试唤起 app,如果 200ms 没有唤起成功,则默认本地没有安装 app,200ms 以后,将会触发下载行为。
结合这个思路,我们来全局考虑一下这个需求应该采用什么样的方案来实现它。
使用 location.href 的同学可能会面临一个担忧,在有的浏览器中,当我们尝试激活 schema link 的时候,若本地没有安装 app,则会跳转到一个浏览器默认的错误页面去了。因此大多数人采用的解决方案都是使用 iframe
测试了很多浏览器,没有发现过这种情况
后来观察了网易新闻,今日头条,YY 等的实现方案,发现大家都采用的是 iframe 来实现。好吧,面对这种情况,只能屈服。
整理一下目前的思路,得到下面的解决方案
想法很美好,现实很残酷。一测试,就发现简单的这样实现有许多的问题。
第一个问题在于,当页面成功唤起 app 之后,我们再切换回来浏览器,发现跳转到了下载页面。
为了解决这个问题,发现各个公司都进行了不同方式的尝试。
也是历经的很多折磨,发现了几个比较有用的事件。
pageshow 页面显示时触发,在 load 事件之后触发。需要将该事件绑定到 window 上才会触发
pagehide 页面隐藏时触发
visibilitychange 页面隐藏没有在当前显示时触发,比如切换 tab,也会触发该事件
document.hidden 当页面隐藏时,该值为 true,显示时为 false
由于各个浏览器的支持情况不同,我们需要将这些事件都给绑定上,即使这样,也不一定能够保证所有的浏览器都能够解决掉这个小问题,实在没办法的事情就不管了。
因此需要扩充一下上面的方案,当本地 app 被唤起,则页面会隐藏掉,就会触发 pagehide 与 visibilitychange 事件
而另外一个问题就是 IOS9+ 下面的问题了。ios9 的 Safari,根本不支持通过 iframe 跳转到其他页面去。也就是说,在 safari 下,我的整体方案被全盘否决!
于是我就只能尝试使用 location.href 的方式,这个方式能够唤起 app,但是有一个坑爹的问题,使用 schema 协议唤起 app 会有弹窗而不会直接跳转去 app!甚至当本地没有 app 时,会被判断为链接无效,然后还有一个弹窗。
这个弹窗会造成什么问题呢?如果用户不点确认按钮,根据上面的逻辑,这个时候就会发现页面会自动跳转到下载去了。而且无效的弹窗提示在用户体验上是不允许出现的。
好吧,继续扒别人的代码,看看别人是如何实现的。然后我又去观摩了其他公司的实现结果,发现网易新闻,今日头条都可以在 ios 直接从微信中唤起 app。真是神奇了,可是今日头条在 Android 版微信上也没办法直接唤起的,他们在 Android 上都是直接到腾讯应用宝的下载里去。所以按道理来说这不是添加了白名单。
为了找到这个问题的解决方案,我在网易新闻的页面中扒出了他们的代码,并整理如下,添加了部分注释(因涉及的代码块篇幅过长,考虑到阅读效果,请至阅读原文查看。
虽然有一些外部的引用,和一些搞不懂是干什么用的方法和变量,但是基本逻辑还是能够看明白。好像也没有什么特别的地方。研究了许久,看到了一个 jsonp 请求很奇特。这是来干嘛用的?
于是费尽千辛万苦,搜索了很多文章,最终锁定了一个关键的名词 Universal links。
如果我早知道这个名词,那么问题就不会变的那么束手无策。所以这个东西是什么呢?
Apple 为 iOS 9 发布了一个所谓的通用链接的深层链接特性,即 Universal links。虽然它并不完美,但是这一发布,让数以千计的应用开发人员突然意识到自己的应用体验被打破。
Universal links,一种能够方便的通过传统的 HTTP/HTTPS 链接来启动 App,使用相同的网址打开网站和 App。
关于这个问题的提问与 universal links 的介绍请至阅读原文
ios9 推行的一个新的协议!
关于本文的这个问题,国内的论坛有许许多多的文章来解决,但是提到 universal links 的文章少之又少,而我想吐槽的是,我们的 ios 开发也尼玛不知道这个名词,搞什么鬼。他改变了用户体验的关键在于,微信没有屏蔽这个协议。因此如果我们的 app 注册了这个协议,那么我们就能够从微信中直接唤起 app。
这个时候我就发现,上面贴的网易新闻代码中的 jsonp 请求的内容,就是这个协议必须的一个叫做apple-app-site-association的 JSON 文件
大家可以直接访问这个链接,查看里面的内容
http://active.163.com/service/form/v1/5847/view/1047.jsonp
至于 universal links 具体如何实现,让 ios 的同学去搞定吧,这里提供两个参考文章
http://www.cocoachina.com/bbs/read.php?tid-1486368.html
https://blog.branch.io/how-to-setup-universal-links-to-deep-link-on-apple-ios-9
支持了这个协议之后,我们又可以通过 iframe 来唤起 app 了,因此基本逻辑就是这样了。最终的调研结果是
没有完美的解决方案
就算是网易新闻,这个按钮在使用过程中也会有一些小 bug,无法做到完美的状态。
因为我们面临许多没办法解决的问题,比如无法真正意义上的判断本地是否安装了 app,pageshow,pagehide 并不是所有的浏览器都支持等。很多其他博客里面,什么计算时间差等方案,根!本!没!有!用!我还花了很久的时间去研究这个方案。
老实说,从微信中跳转到外部浏览器,并不是一个好的解决方案,这样会导致很多用户流失,因此大家都在 ios 上实现了 universal links,而我更加倾向的方案是知乎的解决,他们从设计上避免了在一个按钮上来判断这个逻辑,而采用了两个按钮的方式。
网易新闻的逻辑是,点击打开会调整到一个下载页面,这个下载页面一加载完成就尝试打开app,如果打开了就直接跑到 app 里面去了,如果没有就在页面上有一个立即下载的按钮,按钮行只有下载处理。