一:需求说明:
做过客户端开发的应该都遇到过分享APP内内容至第三方平台吧;
一般而言,主要是分享至QQ,微信,微博,QQ空间等平台;
对于一些国际化的APP可能会有分享至Facebook,twitter等平台的要求;
因为公司APP主要面对的是国内用户,所以下面只考虑国内的一些平台,国外不做兼容;
二:H5打开APP指定页面跳转要求
-
1、用户已安装应用
- 点击打开直接将相关参数传递至客户端,打开APP指定页面
-
2、未安装应用
- 提示用户下载,或跳转至App Store
三:通过H5打开APP的几种写法
方法一、利用HTML的a标签,通过设置href,在用户点击之后直接跳转
//直接在HTML中设置跳转地址
<a href="dsblockchain://">打开星域</a>
//通过jQuery设置跳转
$("#openapp").attr("href", "dsblock://");
方法二、通过ifr.src设置跳转链接
var ifr = document.createElement('iframe');
ifr.src = config.scheme_IOS;
ifr.style.display = 'none';
document.body.appendChild(ifr);
方法三、通过设置window.locatin或者window.location.href打开
window.location.href = config.download_url;
四:根据需求初次实现方案如下:(后期改进采用方案二)
-
方案一逻辑如下:
- 所有逻辑判断,跳转都在分享出去的当前H5中完成;
- 点击打开按钮
- 已安装:打开指定页面,
- 未安装:下载
- 因为H5无法像客户端一样知道用户当前安装的程序,所以无法直接通过包名的方式打开APP
- 处理方式:点击打开之后直接尝试打开客户端同事提供的scheme,然后设置一个延时任务,当用户在一定时间间隔离开了当前页面,就认为用户安装打开了APP,直接清除掉延时任务,不执行打开App Store,或者下载APP的任务;
- 微信、微博无法直接打开APP以及下载应用,直接提示浏览器打开
方案一代码如下
//打开app按钮
<span id="open"></span>
//点击之后的相关判断
var config = {
scheme_IOS: 'dsblock://', //iOS发开给
scheme_Adr: 'block://ds/wakeapp', //Android开发给
download_url: '下载链接',
timeout: 2000
};
openclient();
function openclient() {
var startTime = Date.now();
//---点击打开之后--尝试打开APP---start------
var ifr = document.createElement('iframe');
if (navigator.userAgent.match(/Android/i) != null) {//Android
//传递参数给客户端
ifr.src = (config.scheme_Adr + "?Type=2&"+"MatchId="+matchid);
}else if(navigator.userAgent.match(/(iPhone|iPad|iPod|iOS)/i) != null) {//iOS
ifr.src = (config.scheme_IOS + "?Type=2&"+"MatchId="+matchid);
} else {//PC端无法打开app。点击后直接下载安卓安装包即可
ifr.src = "下载链接";
}
ifr.style.display = 'none';
document.body.appendChild(ifr);
//---点击打开之后--尝试打开APP---end------
//根据当前平台修改打开失败的跳转链接
if (navigator.userAgent.match(/(iPhone|iPad|iPod|iOS)/i) != null) {//iOS跳至App Store
config.download_url = 'APP在App Store的链接地址';
} else if (navigator.userAgent.match(/Android/i) != null) {//安卓直接下载
config.download_url = '下载链接';
}else{//PC
config.download_url = '下载链接';
}
//打开失败要执行的操作
var t = setTimeout(function () {
var endTime = Date.now();
if (!startTime || endTime - startTime < config.timeout + 200) {
window.location = config.download_url;
}
}, config.timeout);
//当前页面失去焦点,清除延时任务,不提示用户下载
window.blur = function () {
clearTimeout(t);
}
}
-
方案一存在的缺陷
- 缺点:用户已安装APP,但是打开之后又被拉回到QQ或者浏览器页面,提示下载。
- 原因:当用户打开APP后,window.blur方法没有回调,延时任务没被清除掉,还是提示用户下载了;
而且因为这个提示,用户界面也会被拉回到QQ或者浏览器中;
- 原因:当用户打开APP后,window.blur方法没有回调,延时任务没被清除掉,还是提示用户下载了;
- 小改进:Android下载链接换成APP在应用宝的微下载链接,这样用户打开APP之后就算延时任务没被清除,也不会提示下载。只会打开APP
- 测试后存在的问题:如果在QQ里面APP已经打开了,当用户打开APP之后,因为延时任务没被清除掉,会跳转到应用宝的推广链接,里面检测到用户已安装该APP,就直接打开了APP,但是只会打开APP并不会携带参数跳转至指定页。导致用户已跳转到指定页之后又被拉到首页了,所以还是无法满足要求直接PASS掉。
- 缺点:用户已安装APP,但是打开之后又被拉回到QQ或者浏览器页面,提示下载。
-
方案一总结
- 方案一经过一次更换下载链接为应用宝推广链接之后,还是无法满足要求,无法打开指定页面;
- 因为要保证Android和iOS两端逻辑流程大致一样,所以经测试Android无法打开之后直接将该方案废弃,iOS也没有进行测试了;
五:最终方案
方案一被pass掉后就一直在找相关的代替方法,在网上找了一家提供类似服务的公司,但是经过测试还是存在被拉回到QQ页面的问题;
-
最后在参考斗鱼以及TapTap之后将方案调整如下:
- 用户点击打开APP之后,将原来的跳转链接更换为一个中转的H5,当安卓用户在QQ里面打开之后不在分享的H5里面提示下载,而在中转页提供下载功能
- 斗鱼中转页:http://live.qq.com/api/douyu
- 注:斗鱼在微信里面也可以打开指定页面,应该是微信给斗鱼开了白名单
- TapTap中转页:https://d.taptap.com/taptap/dispatch
- 中转页功能,接收分享页的参数,当用户点击[打开APP继续]之后打开APP指定页
本方案在开发过程中遇到的坑
-
1、三种设置跳转地址方法的兼容问题;
- 在将Android的跳转下载链接更换为跳转到中转页之后,Android基本满足要求,主要是iOS的兼容问题了
-
2、设置跳转链接三种方式对比--------针对iOS平台
- 方法一:a href="dsblock"
- 手机QQ只能通过该方式跳转到APP里面,方法二和方法三都无法跳转,所以当当前是QQ里面打开,就只能通过这种方式设置打开APP的链接了
- 手机QQ和Safari都可以跳转,但是如果没有安装的时候
- 方法二:通过ifr.src设置跳转链接
- 手机QQ无效,Safari浏览器无效
- 方法三:除了手机QQ都可以
- 方法一:a href="dsblock"
-
代码如下
HTML代码 <div class="down_right"> <a href="javascript:void(0);" onclick="fn();" id="openapp">打开星域</a></div> //Js代码 var articleId = getParamValue("ArticleId"); if (isIos()) { if (is_qqbrowser()) {//QQ里面需要通过这种方式才可以打开 $("#openapp").attr("href", createUrl("dsblockchain://", {'Type': 1, 'PostType': 1, 'PostId': articleId})); } } function fn() { if (is_weixin() || isWeibo()) {//微信,微博都无法直接下载,只能打开中转页 window.location.href = "中转页地址" } else { var config = { scheme_IOS: 'dsblock://', scheme_Adr: 'block://ds/wakeapp', download_url: '中转页', timeout: 1000 }; openclient(); function openclient() { var startTime = Date.now(); var ifr = document.createElement('iframe'); if (navigator.userAgent.match(/Android/i) != null) { ifr.src = createUrl("block://ds/wakeapp", {'Type': 1, 'PostType': 1, 'PostId': articleId}); } else if (navigator.userAgent.match(/(iPhone|iPad|iPod|iOS)/i) != null) { window.location.href = createUrl("dsblock://", {'Type': 1, 'PostType': 1, 'PostId': articleId}); } else {//pc端 ifr.src = "apk下载地址"; } ifr.style.display = 'none'; document.body.appendChild(ifr); if (navigator.userAgent.match(/Android/i) != null){ config.download_url = createUrl("中转页.html", {'Type': 1, 'ArticleId': articleId}); }else if (navigator.userAgent.match(/(iPhone|iPad|iPod|iOS)/i) != null){ config.download_url = 'App Store地址'; }else {//pc config.download_url = 'apk下载地址'; } var t = setTimeout(function () { var endTime = Date.now(); if (!startTime || endTime - startTime < config.timeout + 2500) { document.body.removeChild(ifr); window.location.href = config.download_url; } }, config.timeout); window.blur = function () { clearTimeout(t); } } } } //涉及到的相关方法 function is_weixin() { var ua = navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == "micromessenger") { return true; } else { return false; } } function is_qqbrowser() { var ua = navigator.userAgent.toLowerCase(); if (/mqqbrowser|qq/i.test(ua)) { return true; } else { return false; } } function isWeibo() { var ua = window.navigator.userAgent; return !!/__weibo__/.exec(ua); } function isIos() { return navigator.userAgent.match(/(iPhone|iPad|iPod|iOS)/i) != null; } function isSafari() { var ua = window.navigator.userAgent; return !!/Version[|\/]([\w.]+)(\s\w.+)?\s?Safari|like\sGecko\)\sMobile\/\w{3,}$/.exec(ua); } function createUrl(url, obj) {//拼接跳转链接及参数生成新的链接 let params = []; for (let p in obj) { if (obj[p] != null && obj[p] != '') { params.push(p + '=' + obj[p]) } } return url + '?' + params.join("&"); } function getParamValue(name) {//获取地址栏指定参数的参数值 var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if (r != null) { return unescape(r[2]); } else { return null; } }
整理快完成的时候,发现代码还可以优化,遂改为如下代码
var articleId = getParamValue("ArticleId");
if (isIos()) {
if (isWeibo()||is_weixin()){//微博微信打不开APP,(所以要做区分,这里区分和在后面的fn方法中区分都行)直接将跳转地址设置为中转页
//之所以这里区分出微信和微博,而不是直接通过延时任务的location.href直接跳转到App Store,
//是因为跳转到中转页会提示用户在浏览器打开,可以打开指定页面
$("#openapp").attr("href", createUrl("中转页地址", {'Type': 1, 'PostType': 1,'ArticleId': articleId}));
} else{
$("#openapp").attr("href", createUrl("dsblock://", {'Type': 1, 'PostType': 1, 'PostId': articleId}));
}
} else if (navigator.userAgent.match(/Android/i) != null) {
$("#openapp").attr("href", createUrl("block://ds/wakeapp", {'Type': 1, 'PostType': 1, 'PostId':articleId}));
} else {
$("#openapp").attr("href", "apk下载地址");
}
function fn() {
var config = {
scheme_IOS: 'dsblock://',
scheme_Adr: 'block://ds/wakeapp',
download_url: '中转页',
timeout: 1200
};
var startTime = Date.now();
if (navigator.userAgent.match(/Android/i) != null) {
config.download_url = createUrl("中转页地址",{'Type': 1, 'ArticleId': articleId});
} else if (navigator.userAgent.match(/(iPhone|iPad|iPod|iOS)/i) != null) {
config.download_url = 'App Store跳转地址';
} else {//pc置空,因为前面已经设置了,要不然会提示下载多个文件,不过只要调用了window.location,href都会刷新当前页面
config.download_url = '';
}
var t = setTimeout(function () {
var endTime = Date.now();
if (!startTime || endTime - startTime < config.timeout + 2500) {
window.location.href = config.download_url;
}
}, config.timeout);
window.blur = function () {
clearTimeout(t);
}
}
六:中转页部分代码及网页截图
//如果是微博,微信,则隐藏打开按钮,显示下载按钮,显示打开提示文字,其余平台隐藏提示文字,显示下载和打开按钮
tip 顶部提示文字:已安装,在浏览器打开
open: 打开APP,继续按钮
<a class="open-button download-button" id="install">立即安装</a>
<p><a class="open-button" id="open">打开 APP 继续</a></p>
if (browserName === "Wechat" || browserName === "Weibo") {
showDiv("#tip", true);
showDiv("#open", false);
} else {
showDiv("#tip", false);
showDiv("#open", true);
}
//获取地址栏携带的参数,获取之后传递给客户端
var params = window.location.href.split("?")[1];
console.log(params);
if (isIos()) {//iOS
$("#open").attr("href", "dsblockchain://"+"?"+params);
$("#install").attr("href", "App Store推广链接");
} else {//安卓
$("#open").attr("href", "block://ds/wakeapp"+"?"+params);
if (browserName === "Wechat") {//微信无法下载,打开应用宝推广页
$("#install").attr("href", "应用宝推广链接");
} else {
$("#install").attr("href", "APK下载地址");
}
}
function showDiv(divName, isShow) {
if (isShow) {
$(divName).show();
} else {
$(divName).hide();
}
}
总结:
- 从开始调试到现在最终方案的形成差不多花了1周;
- 主要的坑就是window.blur方法没有回调,a标签设置跳转路径,iframe打开连接的兼容问题
-
上面的这些坑估计H5开发大佬分分钟解决,落魄的Android开发流下了没有技术的泪水。。