在H5页面打开APP的方法一般有两种,在IOS 9以前,一般使用的技术是URL Scheme。这种方式虽然可自定义程度很高,能够巧妙地实现很多跳转,但弊端也很明显:我们只能通过 scheme://example 这种格式的链接来实现跳转,而且现在苹果还对这种方式的跳转加了一个提示框:“是否打开XXX”。对于对Web和原生App交互的场景需求量很大的产品来说,这样的跳转方式显然是步骤冗杂的,用户体验并不好。
iOS 9 以后,Universal Links 的出现完美的解决了这个问题。它所提供的直接、顺畅、无缝衔接的跳转能够让用户体验提升一个很大的级别。用户可以点击开发者指定的类似于 https://example.com/t 的URL直接唤醒App,而不需要在浏览器打开再点击其他按钮,实现真上的一键直达,无缝链接。
但是Universal Links只能在IOS 9+的平台才可以使用,我们还需要兼容微信、安卓和IOS 9以前的版本,所以项目中,我们会采用URL Scheme和Universal Links两个结合的方式。
1.技术介绍
1.1 URL Scheme
项目中需要配置 URL Scheme 以用于场景恢复时跳转到应用中(这里的scheme要唯一确定,不要与其他应用一致,建议使用项目名称作为scheme,不然有可能会跳转到其他的应用里)。
在iOS工程中选择工程 Target,选择 Info 选项,然后打开 URL Types,添加 URL Scheme。可以自定义协议名称,如moblink。这个形式的 URI 就会关联跳转到工程中。
配置好后安装APP后,直接在访问设置好的 URL Scheme可以直接打开APP,如平时我们用的比较多的APP,默认的 URL Scheme如下:
- QQ: mqq://
- 微信: weixin://
- 腾讯微博: TencentWeibo://
- 淘宝: taobao://
- 支付宝: alipay://
- 微博: sinaweibo://
使用方式: <a href="weixin://">打开微信</a>
点击a标签就可以打开了微信(如果安装了微信)。
这种方式访问是比较简单的,安卓和IOS都可以使用,但是在微信端就不行了,微信端设置了白名单,完全屏蔽了URL Scheme 用法,除非加入了白名单,像京东就可以直接打开,否则是不可能的,那我们还有下面的办法来解决这个办法。
##1.2 Universal Links
用URL Scheme 不能在微信里打开APP,但是在IOS 9+的系统上还是有解决办法的,那就是用Universal Links,官方网站https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html,这里都是英文的,网上也有很多文章。开篇就说了,如果你单纯为了能让H5打开App,Schema就能做到了,Universal Links的意义则是把普通url,也赋予了能打开App的能力,而不必编写专门的Schema Url去唤起App,在没装App的时候,Universal Links他也是一个合法的url链接,浏览器可以正常跳转,因此不会出现在iOS上讨人厌的框。而且Universal Links目前还没有基于iOS的UI/WKWebView的应用进行拦截,所以目前看还是能突破微信/手百的封锁。(以后,不好说啊~)
2.DEMO示例(IOS篇)
2.1 IOS平台配置
- 在苹果开发者账号下,将本项目的Associated Domains 开关Enable;
- 在工程中选择好本项目的证书和描述文件;如果是已存在的项目,请完成步骤1后重新下载新的描述文件;
- 在工程中打开Associated Domains开关,并设置域名
测试一级域名:aa.tk 测试二级域名:sit.aa.tk
Domains域名 | 作用 |
---|---|
applinks:aa.tk | 服务器主域名 |
applinks:www.aa.tk | |
applinks:sit.aa.tk | 服务器子域名,用于点击自定义按钮跳转app,需要跨域才能生效 |
注:用于跳转打开app的域名需要支持https,如果是一级域名页面有个按钮,点击按钮跳转二级域名来打开app,那么二级域名需要支持https。这里DEMO的二级域名不支持https,所以采用的方案是二级域名跳转到一级域名来打开APP。
相关代码:
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *webpageURL = userActivity.webpageURL;
NSString *host = webpageURL.host;
// 在这里写需要的逻辑,比如跳转到某个详情页
if ([host isEqualToString:@"www.aa.tk"]) {
NSString *url = [userActivity.webpageURL.description substringFromIndex:23];
NSArray *mat = [url componentsSeparatedByString:@"/"];
if (mat.count == 2) {
NSString *type = [mat objectAtIndex:0];
NSString *key = [mat objectAtIndex:1];
if ([type isEqualToString:@"goods"]) {
[self openGoodsView:key];
}
}
}
else{
[[UIApplication sharedApplication] openURL:webpageURL];
}
}
return YES;
}
当通过Universal Link进入app时,会触发此代理,可以从前端传递参数,这里做相应处理,理论上可以跳转到APP的任何一个页面。
##2.2 配置文件apple-app-association
-
配置文件的位置放在跳转域名的根目录,DEMO将一级域名设置为打开APP的地址,所以上传到主域名对应服务器的根目录。判断位置是否正确可以访问"https://www.aa.tk/apple-app-site-association"查看,如果正确显示则正确。
-
配置文件的内容如下:
{ "applinks": { "apps": [], "details": [{ "appID": "xxx", "paths": ["/testlinkapp/*"] }] } }
“appID"是团队ID的值或应用程序ID前缀,紧随其后的是boundle ID。(appID key 就是在你的应用程序的“application-identifier”),苹果开发者账号》merbership下查看teamid,bundle id 为项目自己定义的唯一标识。
“paths"是一个字符串数组,指定你的网站的哪些部分支持跳转到应用程序和哪些网站的部分不想跳转到应用。指定一个域名,不应该作为一个通用的链接处理,在域名paths字符路径的前部添加“NOT”(包括空间T)。
这里设置了”/testlinkapp/*”,也就是说访问"/testlinkapp/“文件夹下任何文件(不管是否存在)都可以,例如"https://www.aa.tk/testlinkapp/xxx”。如果没有跨域,就不能跳转,不过下滑可以显示横幅,点击打开也可以打开。
##2.3 下载安装APP并测试
如果配置文件发生更改,则需要重新下载APP。这里一级域名和二级域名对应的是同一个文件根目录。
测试案例:
环境| 访问url |点击跳转url | 是否跨域 | 结果
- | :-: | -:
浏览器|https://www.aa.tk/testlinkapp/xxx| |否|页面不存在,下滑出现”打开APP“横幅,点击打开会打开APP
浏览器|http://sit.aa.tk/testlinkapp2/demo.html|https://www.aa.tk/testlinkapp/zzz |是|点击demo页的按钮,直接打开了APP
浏览器|https://www.aa.tk/testlinkapp/xxx| |否|直接打开APP
浏览器|http://sit.aa.tk/testlinkapp2/demo.html|https://www.aa.tk/testlinkapp/zzz |是|点击demo页的按钮,直接打开了APP
##2.4 没有安装跳转到APP Store
使用Universal Links时将跳转的页面设置为下载页面,如果APP已经安装就直接打开APP,如果没有安装则进入下载页面。
使用URL Scheme时用延时,超过三秒没有打开APP,则打开下载页面,具体实现后面源码部分。
3.DEMO示例(AOS篇)
安卓不能采用Universal Links的方法,这里使用的是URL Scheme+intent。
##3.1 安卓配置文件
var AppConfig = {
"scheme": "xxx",
"package": "com.xxx",
"action": "android.intent.action.VIEW",
"category": "android.intent.category.BROWSABLE",
"host":"www.xxx.com",
"FAILBACK_ANDROID":"https://www.xxx.com/xxxx",
"FAILBACK_IOS":"itms://itunes.apple.com/hk/app/bochk/xxx?mt=8"
};
这里的参数由安卓同事提供,“FAILBACK_ANDROID”是AOS没有打开app跳转到下载页面的链接
“FAILBACK_IOS”是IOS没有打开app跳转的下载链接,这两个是前端配置的。
在Chrome浏览器中使用intent方法,在非Chrome浏览器使用URL Scheme,Chrome中使用intent的好处:Chrome可以识别S.browser_fallback_url参数,如果没有安装app,则直接跳转,而不用我们自己判断是否安装app,在非Chrome中通过延时判断是否安装app会有弊端:(用schema方法实现)点击打开app,会有提示“是否用XXX应用打开”的提示框,如果用户没有点击按钮超过设置的时间,页面就直接跳转了下载页,体验不好。具体实现看源码。
##3.2 安卓端在微信端暂时没有办法,只能跳转应用宝或者下载页面。
4.Universal Links坑及建议
##4.1 配置apple-app-association
- 域名必须支持https
- 域名根目录下放这个文件apple-app-association,不带任何后缀
- 文件为json格式保存为文本即可
- json按着官网的要求填写即可
测试是否正确,直接访问域名+配置文件名如果能正确访问则放置的位置是正确的。例如你想通过访问"https://aa.test.com/xxx"来打开app,那么你要把配置文件放在"https://aa.test.com/"对应服务器根目录,通过访问"https://aa.test.com/apple-app-association"能直接访问配置文件则是正确的。
##4.2 配置Domains
- 开发者中心证书打开Associated Domains
- 工程配置Associated Domains
- 将你apple-app-association所在域名配置进去
- 给你的工程像Schema的OpenUrl一样,编写App被唤醒后的处理逻辑
在配置Domains时,我们需要将一级域名和二级域名都要添加进去,这样做是为了通过自定义按钮打开app,而这种做法需要跨域,所以需要配置两个,如果用二级域名来打开app,则二级域名也需要支持https。
##4.3 Universal Links基本运作流程
- APP第一次启动 or APP更新版本后第一次启动
- APP向工程里配置的域名发起Get请求拉取apple-app-association Json File
- APP将apple-app-association注册给系统
- 由任意webview发起跳转的url,如果匹配了apple-app-association注册过的通用链接(path字段配置)
- 打开App,触发Universal Link delegate
- 没匹配,webview继续跳转url(当普通的url跳转)
##4.4 跨域
Universal Links必须要求跨域,如果不跨域,就不能实现自定义按钮打开APP,经测试如果不跨域,直接输入配置的url,会跳转到url,下滑会出现如下的横幅,点击打开也可以打开app。
这里我们用跨域主要是用于自定义按钮打开App,如果打开一个页面"http://www.aa.com/index.html",这个页面里面有个”打开APP“的按钮,点击按钮去打开APP。我们用Universal Links技术就是点击按钮让页面跳转到我们在apple-app-association配置文件里配置好的路径里去,不管访问的文件是否存在,都能打开APP。例如我们设置的Universal Links的域名为二级域名"https://bb.aa.com/",配置文件的path为"/demo/*",我们将按钮的跳转链接设置为"https://bb.aa.com/demo/xxx",这时就可以打开APP。
PS:
1.这里配置文件就要放在二级域名的根目录
2.xxx可以不存在,也可以存在
3.如果没有跨域的话,会当普通的url跳转
4.二级域名同样要添加到Domain里
5.跨域最好使用二级域名的方式
##4.5 Universal Links会因为用户的行为而失效
Universal LInks触发后打开APP,这时候APP的状态栏右上角会有文字提示来自XXX App,可以点击状态栏的文字快速返回原来的APP,如果用户点击了返回微信,就会被iphone记住,认为用户不需要跳出原APP打开新APP,因此这个APP的Universal Links会被关闭,再也无效。还有在备忘录里长按Universal Links链接会出现"用XXApp打开"的选项,如果你点击了"用Safari浏览器打开",那么点击此链接也会默认用浏览器打开url,而不是打开APP。
想要开启也不是不行,让用户重新用safari打开,Universals Links的页面,然后会出现很像苹果smart bar的东西,那个东西点了后就能打开。
##4.6 判断是否安装了APP及跳转APP Store
我们要做的时如果用户安装了APP就直接打开APP,如果没有安装就打开下载页面或者进入应用市场让用户下载应用,但是在前端时无法通过js去判断是否已经安装了APP,解决办法:
1.用URL Scheme时用延时。设置3秒延时,如果用户安装了APP,3秒内没有打开则进入下载页面或应用市场,弊端是用URL Scheme会有询问框“是否在XXXAPP中打开”,如果用户3秒内没有点击,则页面就跳转到了下载页。
2.安卓端Chrome浏览器可以用intent配置S.browser_fallback_url来使没有打开app时直接进入下载页面。
3.用Universal Links打开的页面设置为下载页,下载页里放置下载按钮,点击下载按钮进入应用市场。如果已经安装了APP,则直接打开APP,如果没有安装,则这个下载url会被当作普通链接,浏览器就能打开下载页,点击下载按钮,打开APP Store。
5.源码
源码地址:点击下载
6.实现的功能
IOS实现功能:
1.在浏览器中点击,若没有安装app,会跳转到下载页,点击下载按钮,打开itunes应用市场;
2.在浏览器中点击,若安装了app,则直接打开app;
3.在微信页面中点击按钮,若没有安装app,会跳转到下载页,点击下载按钮,打开itunes应用市场;
4.在微信页面中点击按钮,若安装了app,则直接打开app;
5.在微信消息中直接点击打开app的链接,可以直接打开app.(不是在微信浏览器中)
6.在记事本,短信,邮件中直接打开app
AOS实现功能:
1.在浏览器中点击,若没有安装app,3秒会跳转到下载页;
2.在浏览器中点击,若安装了app,则直接打开app;
3.在Chrome浏览器,没有 超过3秒后造成页面跳转到下载页 的问题