美文网首页收藏夹
H5打开APP指定页面爬坑记录

H5打开APP指定页面爬坑记录

作者: 没有故事的男童鞋 | 来源:发表于2018-11-27 12:02 被阅读163次

    一:需求说明:

    做过客户端开发的应该都遇到过分享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或者浏览器中;
      • 小改进:Android下载链接换成APP在应用宝的微下载链接,这样用户打开APP之后就算延时任务没被清除,也不会提示下载。只会打开APP
        • 测试后存在的问题:如果在QQ里面APP已经打开了,当用户打开APP之后,因为延时任务没被清除掉,会跳转到应用宝的推广链接,里面检测到用户已安装该APP,就直接打开了APP,但是只会打开APP并不会携带参数跳转至指定页。导致用户已跳转到指定页之后又被拉到首页了,所以还是无法满足要求直接PASS掉。
    • 方案一总结

      • 方案一经过一次更换下载链接为应用宝推广链接之后,还是无法满足要求,无法打开指定页面;
      • 因为要保证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都可以
    • 代码如下

      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);
        }
    }
    

    六:中转页部分代码及网页截图

    中转页.png
    //如果是微博,微信,则隐藏打开按钮,显示下载按钮,显示打开提示文字,其余平台隐藏提示文字,显示下载和打开按钮
    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开发流下了没有技术的泪水。。


      129156.jpg

    相关文章

      网友评论

        本文标题:H5打开APP指定页面爬坑记录

        本文链接:https://www.haomeiwen.com/subject/ilfsqqtx.html