背景需求:
在H5网页中显示悬浮窗口,一般为首页顶部提示;若用户未安装 app 时,引导用户下载 app,若用户已安装app ,则引导用户打开app ,实现网页与app 的无缝链接
调研
国外:
基于firebase: firebase.google.cn/docs
国内:
openinstall: www.openinstall.io
branch: cdn.branch.io/example.htm…
解决方案:
本文在项目中主讲基于firebase的deeplink技术唤醒App
效果展示
Deeplink
背景:
从字面上理解为深度链接,拉新,促活,引流等手段,为提高用户活跃度促进用户的分享,需要支持Deeplink相关功能。
方案:
基于Firebase的Deeplink:
主要流程图如下:
其中需要特别说明的是下载安装后打开指定页面的功能,该功能目前可以有两个实现方案:
App自己实现,主要通过剪切板实现,H5在跳转App Store之前将OpenUrl加密转存到剪切板,在用户安装完打开App时扫描剪切板,扫描到则解析OpenUrl跳转指定页面,并清空剪切板内容
通过后台实现,H5跳转App Store之前将设备ID和OpenUrl上传后端,在安装完App打开时调用接口查询OpenUrl(Firebase使用)
如下图所示:
两个方案各有优势,剪切板方便集成,不依赖后端,相对简单,但是有一定局限性,必须用户安装完App后打开App,否则可能出现问题。
后端方案不受限于App何时打开,只要后端保存有设备ID既可以随时打开指定页面,同时可以记录链接点击次数的需求,但是开发难度较大,需前后端一起配合。
技术实现:
- index.html引入firebase
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.9.1/firebase-app.js"></script>
- app.js初始化firebase
//window.js文件
window.app.firebaseInfo = { //firebaseConfig后管配置
domainUriPrefix = ''
firebaseConfig = {
apiKey: '',
authDomain: '',
databaseURL: '',
projectId: '',
storageBucket: '',
messagingSenderId: '',
appId: ''
}
}
// app.js文件 Initialize Firebase
const firebaseInfo = window.app.firebaseInfo
if (firebase) {
firebase.initializeApp(firebaseInfo.firebaseConfig)
}
- 使用,点击打开app按钮
# Firebase文档:
firebase.google.com/docs/refere…
firebase.google.cn/docs/dynami…
- 添加设备判断及生成短连接
// 设备判断
judgeDevice = () => {
const ua = navigator.userAgent.toLowerCase()
const isAndroid = ua.indexOf('android') > -1
const isIOS = !!ua.match(/(i[^;]+;( u;)? cpu.+mac os x/) //判断是否是 iOS终端
const isFacebookApp = ua.indexOf('fban') > -1 || ua.indexOf('fbav') > -1
const isLineApp = ua.indexOf('line') > -1
const isWeixin = ua.indexOf('micromessenger') > -1
// 安卓设备,且在微信、facebook、line中打开
if (isAndroid && (isFacebookApp || isWeixin || isLineApp)) {
// 添加蒙层引导
const $body = document.getElementsByTagName('body')[0]
//创建一个新元素插入原组件
let divCover = document.createElement('div')
divCover.innerHTML = `<p class="smart_app_text">點擊“\
<span class=${
isLineApp ? 'smart_app_text_line' : ''
}>...</span>”選擇使用瀏覽器打開</p>\
<img class="smart_app_img" src=${app.icons.ARROW} />`
divCover.setAttribute('class', 'smart_app_cover')
//插入到原父元素中
$body.appendChild(divCover)
// 关闭蒙层操作
function closeMask() {
let modalMask = document.getElementsByClassName('smart_app_cover')[0]
modalMask.addEventListener('click', closeMask, false)
$body.removeChild(modalMask)
}
divCover.addEventListener('click', closeMask, false)
}
this.openApp()
}
// 生成短连接
openApp = () => {
const firebaseInfo = window.app.firebaseInfo
const host = `https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=${
firebaseInfo.firebaseConfig.apiKey
}`
// 针对活动列表特殊处理
let link = window.location.href
link =
link.indexOf('/shop/productlist?activityId') > -1
? `${window.app.pathName}activityArea`
: link
let dynamicLinkInfo = {
dynamicLinkInfo: {
domainUriPrefix: firebaseInfo.domainUriPrefix,
link: link,
androidInfo: {
androidPackageName: '' // Android 应用程序的包名称
},
iosInfo: {
iosBundleId: '', // iOS 应用程序的捆绑包 ID
iosAppStoreId: '' // 应用的AppStore ID,用于在未安装应用时将用户引导至AppStore
},
navigationInfo: {
enableForcedRedirect: true
}
},
suffix: {
option: 'SHORT'
}
}
if (window.app.getQueryString('utm_source')) {
dynamicLinkInfo.dynamicLinkInfo.analyticsInfo = {
googlePlayAnalytics: {
utmSource: window.app.getQueryString('utm_source') || '',
utmMedium: window.app.getQueryString('utm_medium') || '',
utmCampaign: window.app.getQueryString('utm_campaign') || ''
}
}
}
window.app
.posts('', JSON.stringify(dynamicLinkInfo), host, 'baseHeader')
.then(res => {
window.location.href = res.shortLink
})
}
唤端技术
唤端技术我们也称之为deep link技术。当然,不同平台的实现方式有些不同,一般常见的有这几种,分别是:
- URL Scheme(通用)
- Universal Link (iOS)
- App Link、Chrome Intents(android)
URL Scheme(通用)
这种方式是一种比较通用的技术,各平台的兼容性也很好,它一般由协议名、路径、参数组成。这个一般是由Native开发的同学提供,我们前端同学再拿到这个scheme之后,就可以用来打开APP或APP内的某个页面了。
URL Scheme 组成
[scheme:][//authority][path][?query][#fragment]
常用APP的 URL Scheme
APP | 微信 | 支付宝 | 淘宝 | 知乎 | |
---|---|---|---|---|---|
URL Scheme | weixin:// | alipay:// | taobao:// | mqq:// | zhihu:// |
打开方式
- 直接通过window.location.href跳转
window.location.href = 'zhihu://'
- 通过iframe跳转
const iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = 'zhihu://'
document.body.appendChild(iframe)
- 直接使用a标签进行跳转
- 通过js bridge来打开
window.miduBridge.call('openAppByRouter', {url: 'zhihu://'})
判断是否成功唤起
Q: 当用户唤起APP失败时,我们希望可以引导用户去进行下载。那么问题是怎么才能知道当前APP是否成功唤起?
A: 可以监听当前页面的visibilitychange事件,如果页面隐藏,则表示唤端成功,否则唤端失败,跳转到应用商店。
实现
假如手机上并没有安装淘宝app,所以也就无法唤起,此时应该跳到应用商店对应的应用下载页,实现如下:
<template>
<div class="open_app">
<div class="open_app_title">测试Demo</div>
<div class="open_btn" @click="open">打开淘宝</div>
</div>
</template>
<script>
let timer
export default {
name: 'openApp',
methods: {
watchVisibility() {
window.addEventListener('visibilitychange', () => {
// 监听页面visibility
if(document.hidden) {
// 如果页面隐藏了,则表示唤起成功,这时候需要清除下载定时器
clearTimeout(timer)
}
})
},
open() {
timer = setTimeout(() => {
// 没找下载页,则跳转到store
window.location.href = 'http://apps.apple.com/cn/app/id387682726'
}, 3000)
window.location.href = 'www.xxx.com://'
}
}
}
</script>
<style lang="less">
.open_app_title {
font-size: (20/@rem);
}
.open_btn{
margin-top:(20/@rem);
padding:(10/@rem) 0;
border-radius: (8/@rem);
background: salmon;
color: #fff;
font-size: (16/@rem);
}
</style>
适用性
URL Scheme 这种方式兼容性好,无论安卓或者 iOS 都能支持,是目前最常用的方式。
但它也有一些比较明显的缺点:
无法准确判断是否唤起成功,因为本质上这种方式就是打开一个链接,并且还不是普通的 http 链接,
所以如果用户没有安装对应的 APP,那么尝试跳转后在浏览器中会没有任何反应,通过定时器来引导用户跳到应用商店,但这个定时器的时间又没有准确值,不同手机的唤端时间也不同,我们只能大概的估计一下它的时间来实现,一般设为3000ms左右比较合适;从上面实现上可以看到会有一个弹窗提示你是否在对应 APP中打开,这就可能会导致用户流失;
有 URL Scheme 劫持风险,比如有一个 app 也向系统注册了 taobao:// 这个 scheme ,唤起流量可能就会被劫持到这个 app 里;
容易被屏蔽,app 很轻松就可以拦截掉通过 URL Scheme 发起的跳转,比如微信内经常能看到一些被屏蔽的现象。
Universal Link (iOS)
Universal Link 是在iOS 9中新增的功能,使用它可以直接通过https协议的链接来打开 APP。
它相比前一种URL Scheme的优点在于它是使用https协议,所以如果没有唤端成功,那么就会直接打开这个网页,不再需要判断是否唤起成功了。并且使用 Universal Link,不会再弹出是否打开的弹出,对用户来说,唤端的效率更高了。
原理
- 在 APP 中注册自己要支持的域名;
- 在自己域名的根目录下配置一个 apple-app-site-association 文件即可。
打开方式
openByUniversal () {
// 比如打开知乎问题页
window.location.href = 'https://oia.zhihu.com/questions/64966868'
},
适用性
相对 URL Scheme,universal links 有一个较大优点是它唤端时没有弹窗提示是否打开,提升用户体验,可以减少一部分用户流失;
无需关心用户是否安装对应的APP,对于没有安装的用户,点击链接就会直接打开对应的页面,因为它也是http协议的路径,这样也能一定程度解决 URL Scheme 无法准确判断唤端失败的问题;
只能够在iOS上使用
只能由用户主动触发
App Link、Chrome Intents(Android)
App Link
在2015年的Google I/O大会上,Android M宣布了一个新特性:App Links让用户在点击一个普通web链接的时候可以打开指定APP的指定页面,前提是这个APP已经安装并且经过了验证,否则会显示一个打开确认选项的弹出框,只支持Android M以上系统。
App Links的最大的作用,就是可以避免从页面唤醒App时出现的选择浏览器选项框;
前提是必须注册相应的Scheme,就可以实现直接打开关联的App。
- App links在国内的支持还不够,部分安卓浏览器并不支持跳转至App,而是直接在浏览器上打开对应页面。
- 系统询问是否打开对应App时,假如用户选择“取消”并且选中了“记住此操作”,那么用户以后就无法再跳转App。
Chrome Intents
- Chrome Intent 是 Android 设备上 Chrome 浏览器中 URI 方案的深层链接替代品。
- 如果 APP 已安装,则通过配置的 URI SCHEME 打开 APP。
- 如果 APP 未安装,配置了 fallback url 的跳转 fallback url,没有配置的则跳转应用市场。
「 App Link、Chrome Intents;这两种方案在国内的应用都比较少。」
解决方案方案对比
Q&A | URL Scheme | Universal Link | App Link |
---|---|---|---|
<ios9 | 支持 | 不支持 | 不支持 |
>=ios9 | 支持 | 支持 | 不支持 |
<android6 | 支持 | 不支持 | 不支持 |
>=android6 | 支持 | 不支持 | 支持 |
是否需要HTTPS | 不需要 | 需要 | 需要 |
是否需要客户端 | 需要 | 需要 | 需要 |
无对应APP时的现象 | 报错/无反应 | 跳到对应的页面 | 跳到对应的页面 |
总结:
「URI Scheme:」
URI Scheme的兼容性是最高,但使用体验相对较差:
当要被唤起的APP没有安装时,链接报错,页面无反应。体验感差!
当注册有多个scheme相同的时候,无法区分。
不支持从其他app中的UIWebView中跳转到目标APP, 所以ios和android都出现了自己的独有解决方案。
「Universal Link」
若用户已安装APP,则直接唤起APP;反之则跳转到对应的web link。
universal Link 是从服务器上查询是哪个APP需要被打开,所以不会存在冲突问题
universal Link 支持从其他app中的UIWebView中跳转到目标app
缺点:
此方案会记住用户的选择:在用户点击了Universal link之后,iOS会去检测用户最近一次是选择了直接打开app还是打开网站。一旦用户点击了这个选项,他就会通过safiri打开你的网站。并且在之后的操作中,默认一直延续这个选择,除非用户从你的webpage上通过点击Smart App Banner上的OPEN按钮来打开。
「App link」
优点与 Universal Link 类似
缺点在于国内的支持相对较差,在有的浏览器或者手机ROM中并不能链接至APP,而是在浏览器中打开了对应的链接。
在询问是否用APP打开对应的链接时,如果选择了“取消”并且“记住选择”被勾上,那么下次你再次想链接至APP时就不会有任何反应
以上调研及解决方案实现,如果对你有所参考和帮助,请加个关注、点个赞吧!感谢