h5唤起app方法

一、引言

有这么一个需求,点击h5页面上的一个按钮,需要判断本机有没有安装某app,若已经安装过,则直接调起此app,若没有安装,则跳转该app的下载页。要求安卓手机跳转此app的下载包地址。ios跳转此app的APP Store。

二、唤起方法

URL Schemes

[scheme:][//authority][path][?query][#fragment] 比如:app://weixin?test=1
  
   行为(应用的某个功能)    
            |
scheme://[path][?query]
   |               |
应用标识       功能需要的参数

URL Schemes:好比给手机APP分配一个特殊格式的 URL,用来访问这个APP或者这个APP中的某个功能(来实现通信)。APP得有一个标识,好让我们可以定位到它,它就是 URL 的 Scheme 部分。

*注意:应用是否支持URL Schemes要看App开发者有没有写那部分的代码了

Intent

安卓的原生谷歌浏览器从chrome25版本之后就不能通过URL Schemes唤醒安卓应用。要使用谷歌官方提供的intent:预发, 如果唤醒失败,则会跳转到谷歌的应用市场。语法与URL Schemes及其相似,相当于谷歌定制版的URL Schemes,也没用过,就不多说。

IOS Universal Link

  • 简介

    Universal Link是在iOS9引入的新功能,通过传统的HTTP链接就可以唤醒app,如果用户没有安装APP,则会跳转到该链接对应的页面,而且在唤醒app的时候没有弹框提示哦。可以说是解决了URL Schemes的大部分问题。

  • 原理及流程

    1. App开发人员去配置中心配置Associated Domain配置一个支持https的域名,比如app-support.test.com - 然后 app-support.test.com/apple-app-site-association或者app-support.test.com/apple-app-site-association/.well-known/apple-app-site-association要返回app的teamId,bundleId,paths信息.

      router.get('/apple-app-site-association, (req, res) => {
        const data = {
          applinks: {
            apps: [],
            details: [
              {
                appID: 'teamId.bundleId',
                paths: ['*']
              }
            ]
          }
        };
        res.set('Content-Type', 'text/html');
        res.send(JSON.stringify(data));
      });
      
      
      
    2. 然后APP安装后首次打开,如果Associated Domain配置了的话,就会去请求app-support.test.com/apple-app-site-association。系统会根据返回的teamId,bundleId,paths知道当打开app-support.test.com下的哪些路径的时候唤醒对应的app,比如paths=*的话,就是打开app-support.test.com下的任意路径都会唤醒app

    3. app那边会收到对应的路径,然后要根据path写逻辑跳转到对应的功能

  • 如何验证配置成功

    在备忘录中输入配置好的链接,直接点开这个链接(https://app-support.test.com),配置好的话会直接跳到app, 或者长按,弹出菜单中会提示在xxx中打开 - 在safari中。

三、常见唤醒媒介

  • iframe
ifr = document.createElement("iframe");
ifr.setAttribute("src", "wrjk://com.eko123"); /***打开app的协议,有an同事提供***/
ifr.style.display = "none";
document.body.appendChild(ifr);

iframe方案的唤起原理是: 程序切换到后台时,计时器会被推迟(计时器不准的又一种情况)。如果app被唤醒那么网页必然就进入了后台,如果用户从app切回来,那么时间一般会超过2s;若app没有被唤起,那么网页不会进入后台,setTimeout基本准时触发,那么时间不会超过2s。

在未安装 app 的情况下,不会去跳转错误页面。但在各个系统及应用中兼容性比较多。例如:ios9+ 禁止掉了iframe方式。

  • a 标签

    a = document.createElement("a");
    a.setAttribute("href", "wrjk://com.eko123"); /***打开app的协议,有an同事提供***/
    a.style.display = "none";
    document.body.appendChild(a);
    

    a标签如果目标scheme错误,即应用不存在也不会报错

  • window.location跳转

    window.location.href = "wrjk://com.eko123";
    

    兼容性:URL Scheme 在 ios 9+ 上诸如 safari、UC、QQ浏览器中, iframe 均无法成功唤起 APP,只能通过 window.location 才能成功唤端。(本人没测试过,摘自别人的帖子)

三种唤醒媒介对比

某篇博文中对三种唤起方式进行了测试

。X表示唤起失败,√表示唤起成功
。红色标记表示进入页面直接唤起,绿色表示人工事件操作后唤起
。ios测试机:iphone 6p;android测试机:小米1s
  • iframe唤起app测试结果

    14657587-3a6cae2885fc2d63.jpg

  • a标签唤起app测试结果

    14657587-e4d90cde88ffa81a.jpg

  • iframe和window.location.href唤起对比

    14657587-5dd8cd13b99d7511.jpg

  • iframe、window.location.href和a标签唤起三者对比

    14657587-5abb787d252c9169.jpg

四、总结

  1. 对于ios来说,location.href跳转更合适,因为这种方式可以在Safari中成功唤起app。Safari作为iphone默认浏览器其重要性就不用多说了。
  1. 对于Android来说,在进入页面直接唤起的情况下,iframe和location.href是一样的,但是如果是事件驱动的唤起,iframe唤起的表现比location.href要更好一点。
  1. 通过测试可以发现,进入页面直接唤起和事件驱动的唤起,对于很多浏览器,两者的表现是不同的,简单来说,直接唤起的失败更多。
  1. 以上测试可能随时间已经有出入和变化,仅供参考。

五、使用中常见问题及解决方案

  1. 问题:可能会被app禁掉,比如微信,qq等

    解决:通常会检测打开的app环境,如果是微信,qq等环境,提示用户浏览器内打开。

  2. 问题:ios9+ 禁止掉了iframe方式。

    解决:通常会检测ios的版本,ios9+不使用iframe方式,或直接用window.location.href.

  3. 问题:h5无法感知是否唤醒成功.

    解决:一段时间之后自动跳转下载页,或者是依赖setTimeout在浏览器进入后台后进程切换导致的时间延迟判断。

  4. 问题:大部分浏览器需要用户手动触发链接,js自动触发无效。

    解决:浏览器机制,可以设置弹框不在显示。


实际应用

之前已经介绍了要实现的需求,让我们一步一步来吧。我应用的是URL Schemes唤起方法,其实最完美的方案是IOS Universal Link,因为这个方法需要app那边配置很多东西,配置好后给前端提供一个地址调用。首先要获取访问设备的信息,代码如下:

let globalData = {
  isMobile: false,//是否为移动端
  openIn: '',//页面打开的app
  openInType: '',//移动端设备的类型 ios an
  isHW: false,//是否为华为浏览器
}
var browser = {
  versions: function () {
    var u = navigator.userAgent, app = navigator.appVersion;
    return {         //移动终端浏览器版本信息
      trident: u.indexOf('Trident') > -1, //IE内核
      presto: u.indexOf('Presto') > -1, //opera内核
      webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
      gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
      mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
      ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
      android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或uc浏览器
      iPhone: u.indexOf('iPhone') > -1, //是否为iPhone或者QQHD浏览器
      iPad: u.indexOf('iPad') > -1, //是否iPad
      webApp: u.indexOf('safari') == -1, //是否web应该程序,没有头部与底部
      //DF:u.indexOf('DF'),//是否是mmc app端
    };
  }(),
  language: (navigator.browserLanguage || navigator.language).toLowerCase(),
}
if (browser.versions.mobile) {//判断是否是移动设备打开。browser代码在下面
  globalData.isMobile = true;
  var ua = navigator.userAgent.toLowerCase();//获取判断用的对象
  console.log(ua + '=============ua对象');
  if (ua.match(/MicroMessenger/i) == "micromessenger") {
    //在微信中打开
    globalData.openIn = 'weixin';
  }
  if (ua.match(/WeiBo/i) == "weibo") {
    //在新浪微博客户端打开
    globalData.openIn = 'weibo';
  }
  if (ua.match(/ qq\//i) == " qq/") {
    //在QQ空间打开
    globalData.openIn = 'qq';
  }
  if (ua.match(/huawei/i) == 'huawei') {
    globalData.isHW = true;
  }
  if (browser.versions.ios) {
    //是否在IOS浏览器打开
    globalData.openInType = 'ios';
  }
  if (browser.versions.android) {
    //是否在安卓浏览器打开
    globalData.openInType = 'android';
  }
} else {
  //否则就是PC浏览器打开
  globalData.isMobile = false;
}

我定义了一个全局对象globalData,用来存储设备信息,有个属性isHW,是因为华为浏览器和其他安卓设备又有些不同,故区分出来单独处理。

此上是设备判断的逻辑,能够得到是pc端打开还是移动端,移动端打开app是什么app(例如:微信,微博,qq等),移动端设备的类型是 ios 还是 an,以及是否是华为手机。

下面就来写调起的程序了。

//唤起app或跳转下载页
function applyApp(){
  if (globalData.isMobile && globalData.openInType == "android") {
    //判断是否是会屏蔽下载链接的app打开
    if (
      globalData.openIn == "weixin" ||
      globalData.openIn == "weibo" ||
      globalData.openIn == "qq"
    ) {
      //Toast("请将链接复制到浏览器中打开");
      $('.mask').show();
      return;
    } else {
      if (globalData.isHW) {
        window.location.href = "wrjk://com.eko123.manmachine/startapp";
      } else {
        ifr = document.createElement("iframe");
        ifr.setAttribute("src", "wrjk://com.ekao123.manmachine/startapp"); /***打开app的协议,有an同事提供***/
        ifr.style.display = "none";
        document.body.appendChild(ifr);
      }
      timer = setTimeout(function () {
        location.href = "http://jrfile.huatu.com/app_version/hlpy/android/wrjk.apk";
      }, 5000);
    }
  }
  if (globalData.isMobile && globalData.openInType == "ios") {
    if (
      globalData.openIn == "weixin" ||
      globalData.openIn == "weibo" ||
      globalData.openIn == "qq"
    ) {
      //Toast("请将链接复制到浏览器中打开");
      $('.mask').show()
      return;
    } else {
      var loadDateTime = new Date();
      window.location = "com.ht.yiqikao://";//schema链接或者universal link
      window.setTimeout(function() { //如果没有安装app,便会执行setTimeout跳转下载页
        var timeOutDateTime = new Date();
        if (timeOutDateTime - loadDateTime < 2000) {
          window.location = "https://itunes.apple.com/cn/app/id1422958471?l=zh&ls=1&mt=8"; //ios下载地址  
        } else {
          window.close();
        }
      }, 2000);
    }
  }
}

至此,唤起app的需求就完成了,仅供大家参考,若有更好的方法,欢迎留言交流。

本文部分内容参考自:https://www.jianshu.com/p/500f4be528e3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342