漫谈Web--H5唤醒

“There is no silver bullet” —Fred Brooks

前言

在信息爆炸的时代,让客户主动去搜索发现,已经越来越难了。如何让精准用户去主动发现,成为APP运营的核心,也是APP推广的重点,特别是在移动互联网的时代,社交成为营销的核心,APP引流是一大亮点,这里引流有两种形式,引导已下载用户打开APP,引导未下载用户下载APP。
从技术的层面来说,就需要一个合理可行的方案来解决如何从H5(网页)中唤醒APP,同时又能给予用户直观且优雅的体验。

浏览器与APP

其实二者彼此根本就是风马牛不相及,浏览器也可以看作是一个应用程序,只不过APP中可以通过WebView控件来获得展示web页面的功能。不管在iOS还是Android上,在浏览器中运行的的网页(脚本)都无法直接获取本机APP相关信息。

一般来说,我们使用的智能设备上有许多我们的个人信息。比如:联系方式、银行卡/信用卡信息、支付宝/Paypal/各大商城的账户密码、照片甚至行程与位置信息等,如何让这些信息仅在设备所有者本人知情并允许的情况下被使用,是所有智能设备与操作系统所要在乎的核心安全问题。针对这个问题,iOS使用了沙盒机制目的就是让APP只能访问它声明可能访问的资源。

URL Scheme

新技术的产生是为了解决问题,解决问题的同时可能也会产生新的问题,毕竟没有银弹。在保障信息安全方面沙盒是个很好的解决办法,但也阻碍了应用间合理的信息共享。URL Schema 应运而生。

什么是URL Scheme

对比我们熟悉的网址URL,Schema其实就是一种特殊的URL。

[scheme://][host][path][?query][#fragment]
https://www.baidu.com/s?wd=paopaolee

像定位一个网页一样,我们可以使用URL Scheme定位一个APP甚至APP里某个具体的功能。

scheme://[path][?query]

其中Scheme对应APP标识, path对应APP的某个功能,query即为功能所需参数。
这里列举出一些常用APP URL Scheme

虽然URL Scheme类似于网页URL,但两者还是有很大区别的,其中:

  1. 网页都有自己的网址,而APP不一定有自己的URL Scheme, 一个 App 是否支持 URL Schemes取决于App开作者是否添加了 URL Schemes相关的代码。

  2. 网页与网址是一一对应的关系,但是由于苹果没有对URL Schemes做唯一性硬性要求,故并非每个URL Schemes都只对应一款应用。(这可能会导致严重的安全性问题)

APP可以向宿主操作系统注册一个URL Scheme,该scheme用于从浏览器或其他应用中启动本应用,通过scheme协议来跳转到相应的APP界面,比如商品详情,活动详情,商家详情等等界面。也可以执行某些指定动作,如完成支付等。也可以在应用内部通过H5页面来直接跳转APP某个界面。

URL Scheme应用场景

  • APP根据服务器下发URL Scheme跳转相应的页面
  • 网页或H5页面通过URL Scheme启动相应APP并且可以跳转具体页面
  • APP通过URL Scheme跳转到另外一个APP指定页面

URL Scheme的不足之处

  1. 现如今诸如微信、微博等主流的APP,已经屏蔽掉了URL Scheme
  2. iOS中使用URL Scheme唤醒APP时,会出现弹窗提示,并且失败也会出现提示窗,用户体验不太友好。
  3. 使用URL Scheme唤醒APP时,无法得知是否唤醒成功。

Android Intent

URL Scheme在Android的Chrome浏览器中存在很大差异,在18及之前的版本中的,URL Scheme能够正常工作,但是从版本25以后,通过URL Scheme已经无法启动一个android app了。取而代之是android 提供了Intent方案。

什么是Intent

Android中提供了Intent机制来协助APP间的交互与通讯。当然,它不仅可用于应用程序之间,也可用于应用程序内部的 Activity / Service之间的交互。它可以更灵活地控制应用程序的启动方式,并且可以通过Intent Extras将额外信息传递到APP。进一步了解Intent

基本语法

基于Intent的URI基本格式如下:

1
2
3
4
5
6
7
8
9
intent:
HOST/URI-path // Optional host
#Intent;
package=[string];
action=[string];
category=[string];
component=[string];
scheme=[string];
end;

当无法解析Intent或无法启动外部APP时,默认会跳转到系统默认的应用程序商店,当然你也可以通过下方的代码选择给Intent定义一个回调URL,这样失败时会将用户重定向到定义的回调URL。

1
S.browser_fallback_url=[encoded_full_url]

示例

为了启动Zxing二维码扫描APP,可以通过如下的方式:

1
<a href="intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;S.browser_fallback_url=http%3A%2F%2Fzxing.org;end"> Take a QR code </a>

对应的Intent如下:

1
2
3
4
5
6
intent:
//scan/
#Intent;
package=com.google.zxing.client.android;
scheme=zxing;
end;

其中//scan/对应HOST/URL-path, com.google.zxing.client.android;对应package, zxing对应scheme,这些参数都是在APP开发配置在Manifest中的。

在iOS9之前,对于从各种从浏览器、Safari中唤醒APP的需求,我们通常只能使用URL Scheme。但是正如我们上面所说,URL Scheme有它自身的不足。其中最主要的是它并没有强制唯一,这可能会带来安全问题。

Universal Link是iOS9推出的一项功能,使你可以通过传统的HTTP链接来启动APP(如果iOS设备上已经安装了你的app,不管在微信里还是在哪里), 或者打开网页(iOS设备上没有安装你的app)。该方案较url scheme有明显的改善。URL Scheme很难做到唯一,而universal link却是你自己控制的,而且Universal Link实现了web-app无缝衔接,没有弹窗提示,用户体验明显好于URL Scheme。
进一步了解Universal Link
默认地,Universal Link能直接打开已安装的APP,无需通过Safari。如果用户没有安装对应的APP时,不同于Schema,Universal Link也是一个合法的URL地址,可以在Safari中打开。

  1. 唯一性: 不像自定义的URL Scheme,Universal 不能够被其它APP声明,因为它是使用标准的HTPPS或HTTP协议来链接到你的网站
  2. 安全性: 当用户安装您的APP时,iOS会检查您上传到您的web服务器的文件,以确保您的网站允许您的APP在其上打开网址。并且只有您可以创建并上传此文件,因此您的网站与APP的关联是安全的
  3. 灵活性: 即使你没有安装对应的APP,Universal Link也能在Safari中打开内容。
  4. 简单行: 一个URL就能使用户在网站和APP间切换。
  5. 私密性: 其它APP无需知晓你的APP是否已经安装也能通信

    ⚠️: 当用户在Safari中浏览您的网站并点击了与当前网页同域的Universal Link时,iOS会直接在Safari中打开该链接。如果是不同域的Universal Link,iOS才会尝试在您的APP中打开。

从加载过程分析Universal Link的不足

一个硬性的不足在于,Universal Link只在iOS9才开始支持,除此之外我们说Universal 就是标准的HTTPS或HTTP链接,那么我们先来看一下一般的URL在WebView中的加载过程,如图所示从解析URL到页面加载完成的过程:
普通URL在WebView中加载过程
那么Universal Link 加载过程又是如何的呢?
Universal Link在WebView中加载过程
相信大家可以看到,Universal Link功能是很容易被封锁掉的,只需要在APP中判断你的域名是否在其允许的。现如今很多主流的APP,它们各自维护着一份白名单,除非你在它的白名单内,否则都会屏蔽掉唤醒操作。尤其是微信,除非腾讯系的APP,其它APP都无法从微信内容中被唤醒,所以淘宝推出了我们熟悉的淘口令

腾讯应用宝对外开放了一个叫做APP Link 的申请,只要你申请了APP Link,就可以通过在打开应用宝的时候在应用宝地址后面添加上 &android_schema={your_scheme} ,来打开指定的页面了。进一步了解应用宝APP Link

唤醒APP的具体实现

上面我们介绍了几种启动APP的解决方案,无论是 URL Scheme 还是 Intent 或者 Universal Link、APP Link,他们都算是URL ,只是URL Scheme 和 Intent 算是特殊的 URL。所以我们可以如同使用URL的方法来使用它们。

iframe

iframe方案的唤醒原理是: 程序切换到后台时,计时器会被推迟(计时器不准的又一种情况)。如果app被唤醒那么网页必然就进入了后台,如果用户从app切回来,那么时间一般会超过2s;若app没有被唤起,那么网页不会进入后台,setTimeout基本准时触发,那么时间不会超过2s。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var last = Date.now(),
doc = window.document,
ifr = doc.createElement('iframe');

//创建一个隐藏的iframe
ifr.src = nativeUrl;
ifr.style.cssText = 'display:none;border:0;width:0;height:0;';
doc.body.appendChild(ifr);

setTimeout(function() {
doc.body.removeChild(ifr);
//setTimeout回小于2000一般为唤起失败
if (Date.now() - last < 2000) {
if (typeof onFail == 'function') {
onFail();
} else {
//弹窗提示或下载处理等
}
} else {
if (typeof onSuccess == 'function') {
onSuccess();
}
}
}, 1000);

在只有 URL Scheme 的日子里,iframe 是使用最多的了。因为在未安装 app 的情况下,不会去跳转错误页面。但是iframe 在各个系统以及各个应用中的兼容问题还是挺多的,不能全部使用 URL Scheme。

a标签

1
<a href="intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;end"">扫一扫</a>

window.location

1
window.location.href = 'sinaweibo://qrcode';

⚠️: URL Scheme在ios 9+上诸如 safari、UC、QQ浏览器中, iframe 均无法成功唤起APP,只能通过 window.location才能成功唤端。当然,如果APP支持Universal Link,ios 9+就用不到URL Scheme了。而 Universal Link 在使用过程中,我发现在 qq 中,无论是 iframe 导航 还是 a 标签打开又或者 window.location都无法成功唤端,一开始我以为是 qq 和微信一样禁止了 Universal Link 唤端的功能,其实不然,百般试验下,通过top.location唤端成功了。

总结

在唤醒功能的实现上没有完美通用的解决方案,我们只能根据特定的业务,在代码层上确保能够覆盖最常用的场景。
这里推荐你两个较为通用的解决方案call-libweb-launch-app

参考文章