首先,简述一下这个需求的背景,产品希望能够让用户在微信内,打开一个h5页面,然后就能唤醒公司中维护的app,这个是为了能够更好的引流。
唤醒app的三种方案
IOS系统-Universal Link(通用链接)
Universal Links可以通过配置指定域名路径直接唤醒APP,一步到位
具体配置看这篇文章
https://juejin.cn/post/6937614343840202766
遇到的问题:
apple-app-site-association文件放在app域名(假设: https://my.app.com/)下
{
"applinks": {
"apps": [],
"details": [
{
"appID": "******",
"paths": [ "/abc/*" ]
},
]
}
}
使用Universal Link其实就是跳转到一个页面(中间页),地址:https://my.app.com/abc/index.html
根据上面配置,这个地址是已经固定了的,这需要跟app域名保持一致,并且在paths配置里面的目录下,为了能够获取到apple-app-site-association文件
const universalLink = 'https://my.app.com/abc/index.html?redirectUrl=' + window.location.href
location.replace(universalLink);
如果未下载app,则会跳转失败,在中间页中处理,跳转失败后再返回到当前页面。
<script>
function getQueryStringArgs(url, opt) {
const { decode = true, multiple = false } = opt || {};
const args = {};
if (!(typeof url === 'string' && url.includes('?'))) return args;
const arr = url.split('?');
const qs = arr.length === 2 ? arr[1] : '';
if (!(typeof qs === 'string' && qs.length)) return args;
const items = qs.split('&');
for (let i = 0; i < items.length; i++) {
const meta = items[i];
if (!(typeof meta === 'string' && meta.includes('='))) continue;
const item = meta.split('=');
const key = decode ? decodeURIComponent(item[0]) : item[0];
const value = decode ? decodeURIComponent(item[1]) : item[1];
if (Object.prototype.hasOwnProperty.call(args, key) && multiple) {
const temp = args[key];
args[key] = Array.isArray(temp) ? [...temp, value] : [temp, value];
} else {
args[key] = value;
}
}
return args;
}
const { redirectUrl } = getQueryStringArgs(location.href)
if (typeof redirectUrl === 'string' && redirectUrl) {
location.replace(redirectUrl + '?callType=universalLink') // 处理唤醒app失败场景
}
</script>
上面这段逻辑如果直接放在html中,最好先手动转一下ES5语法,然后压缩一下,这样兼容性好,上面这样展示,是为了可读性好。
总结:
ios系统使用Universal Link在微信和浏览器内都能够正常的唤醒App,且兼容性比较好。但是需要注意中间页域名需要跟app域名保持一致;唤醒app的h5链接域名不能跟中间页域名一致。
直接扫二维码进入另一个页面,需要进行点击操作才能跳转,IOS不允许打开页面立刻就跳转。
URL-Schemes
URL scheme是App提供给外部的可以直接操作App的规则。
- 比如微信提供了打开扫一扫的URL scheme。weixin://dl/scan
- 比如支付宝提供了转账的URL scheme。alipayqr://platformapi/startapp?saId=20000116
- 比如知乎提供了打开回答页面的URL scheme。zhihu://answers/{id}
如何找到某个app的URL Scheme呢?可以看下面这篇文章
https://zhuanlan.zhihu.com/p/53439246
安卓唤醒app呢,就是使用这种方式
比如:安卓开发提供的是
那跳转的链接是什么样的呢?
const schemeURL = 'myapp://www.myapp.apk'
window.href = schemeURL;
如何判断唤醒失败呢?
没有什么好办法来判断,后面只能触发了唤醒操作之后,监听页面几秒之后是否隐藏来判断,目前默认是2秒
export function getSupportedProperty() {
let hidden;
let visibilityChange;
if (typeof document.hidden !== 'undefined') {
// Opera 12.10 and Firefox 18 and later support
hidden = 'hidden';
visibilityChange = 'visibilitychange';
// @ts-ignore
} else if (typeof document.msHidden !== 'undefined') {
hidden = 'msHidden';
visibilityChange = 'msvisibilitychange';
// @ts-ignore
} else if (typeof document.webkitHidden !== 'undefined') {
hidden = 'webkitHidden';
visibilityChange = 'webkitvisibilitychange';
}
return {
hidden,
visibilityChange,
};
}
/**
* 判断页面是否隐藏(进入后台)
*/
export function isPageHidden() {
const ob = getSupportedProperty();
const hidden = ob?.hidden;
if (typeof hidden === 'undefined') return false;
// @ts-ignore
return document[hidden];
}
/**
* 检测是否唤端成功
* 在唤起执行后,当前页面调用此方法根据页面隐藏变化检测是否唤醒成功
* @param {number} timeout 定时时间,默认2秒
* @return {Object} Promise对象
*/
export function checkOpen(timeout = 2000) {
return new Promise((resolve, reject) => {
const ob = getSupportedProperty();
const visibilityChange = ob?.visibilityChange;
const check = () => {
const pageHidden = isPageHidden();
if (pageHidden) {
resolve(); // 页面被隐藏,说明唤醒成功
} else {
reject(new Error('唤醒超时'));
}
};
const timer = setTimeout(() => {
check();
}, timeout);
const fn = () => {
if (typeof visibilityChange !== 'undefined') {
document.removeEventListener(visibilityChange, fn);
} else {
window.removeEventListener('pagehide', fn);
}
check(); // 唤醒执行后,立马触发页面隐藏变化,可检测是否唤醒成功
clearTimeout(timer); // 未到达指定时间,页面隐藏变化,清除定时器
};
if (typeof visibilityChange !== 'undefined') {
document.addEventListener(visibilityChange, fn);
} else {
window.addEventListener('pagehide', fn);
}
});
}
总结:
安卓使用URL Schemes在微信中是不能跳转的,在浏览器中是能够正常拉起。
微信开放标签
由于在微信环境内,所以可以使用微信提供的能力来唤醒app,微信内禁止使用URL Schemes唤醒app,其实就是微信的一种保护机制。
微信文档:
https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_H5_Launch_APP.html
如上图,使用这个功能,有很多限制,而且需要配置,但是为了安卓用户成功引流,产品还是要求使用这个功能。
微信配置
1.关联App-微信开发平台
微信开发平台配置关联App,关联App需要appId,已经有App的域名
微信开发平台地址: https://open.weixin.qq.com/
2.H5页面域名配置-微信公众平台
JS安全域名需要配置当前h5页面的域名
微信公众号地址: https://mp.weixin.qq.com/
3.初始化微信SDK,需要获取签名
微信开发SDK文档
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
这需要后端开发接口, 去获取签名
使用微信开放标签说明:
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html
async getWxSignatureData() {
const url = window.location.href.split('#')[0];
const res = await getJsapiSignParamers(url);
const { appId, signature, timestamp, nonceStr } = res.data;
wx.config({
debug: false,
appId: appId,
timestamp: timestamp,
nonceStr: nonceStr,
signature: signature,
jsApiList: ['showOptionMenu'], // 必填,故使用一个非实际使用的api用于填充
openTagList: ['wx-open-launch-app'], // 可选,需要使用的开放标签列表
});
wx.ready(() => {
console.info('wx sdk ready');
console.info('调用接口初始化wx sdk 成功');
this.initWxSDKStatus = 'success';
});
wx.error(res => {
console.error('调用接口初始化wx sdk 失败', res);
this.initWxSDKStatus = 'fail';
});
},
接口返回的就是这样的数据结构
只有这样才能正常初始化微信的SDK,只有正常初始化SDK才能够使用微信开放标签的能力。
然后后端开发的时候要注意:签名需要后端配置白名单ip,文档说明如下:
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
安卓手机,如果出现唤醒app之后,打开了应用,但是并未成功唤起,那是因为Android应用有要求,需要安卓开发兼容一下就行了~
微信环境内场景
接下来就分析一下,在微信中有几种分享的场景:
1.微信好友之间链接分享
这种方式,使用微信标签是不能唤醒App的,除非是在关注公众号里面,这个公众号就是上面绑定了JS安全域名的公众号
这样点击这个链接就能正常用微信标签唤醒
2.微信好友之间卡片分享
这种点击打开是能够正常唤醒App的,而且不需要使用公众号,但是这种分享有限制,需要打开页面点击右上角分享给其他好友会带上卡片形式,如果在浏览器中就只是复制链接了,微信不会自动识别成卡片
而且这个分享其实就是微信的一个功能
https://developers.weixin.qq.com/minigame/dev/api/share/wx.onShareAppMessage.html
3.长按识别二维码识别H5链接
这种也能正常唤醒App,而且不需要关注公众号,也很方便,不需要将链接分享给其他人,只需要将唤醒App的链接做出二维码就行了。