h5唤醒app

804 阅读1分钟

h5唤醒app

在App的业务场景中,会出现这样的需求:点击网页广告,如果用户手机安装了该App,那它就会直接打开用户安装的App;如果没安装该App,则会跳转到App下载页面。
这里主要有两种实现方式:

  • url scheme
  • universal links (apple)

url scheme

使用特殊格式([scheme]://[host][:port]/[path]?[query])的URL打开app应用。

实现方式

iframe

每20ms执行一次,执行100次在页面中实际耗费与2000ms不会相差多少。判断条件比预期时间多设置了200ms,所以如果中setInterval内的函数执行100次以内所费时间超过2200ms,则说明APP唤起成功, 打开APP,反之则代表失败,跳转到下载页面。

var $iframe = document.createElement('iframe');
var openTime = +new Date();
var limitNum = 100;
$iframe.src = 'url scheme';
$iframe.style.display = 'none';
document.body.appendChild($iframe); // 用户安装app, 会打开app
var timer = setInterval(function () {
  if (limitNum > 0) {
    limitNum--;
  } else {
    if ((new Date()) - openTime < 2200) {//加了200ms基准误差
      document.body.removeChild($iframe);
      window.location.href = 'your download page'; // 进入下载页面
    }
    clearInterval(timer);
  }
}, 20);

a链接方式

<a href='<scheme域名>://<path>?<params>=<value>'>打开APP</a>

location方式跳转

window.location.href = '<scheme域名>://<path>?<params>=<value>'

在Android / Ios中Url Scheme的实现方式

Android

chrome for Android无法通过iframe方式来调用scheme(安卓会在scheme前自动加上http://),通过a链接的方式可以成功调用。针对chrome内核的浏览器如360浏览器,对于iframe和a链接的方式都能支持,所以对chrome内核的浏览器采用a链接的方式来调用scheme。
对于其他浏览器,如UC,QQ浏览器则采用iframe方式调用scheme。

iOS

ios 9.0之前,可以通过iframe方式调用scheme。
ios 9.0之后,iframe方案变得不可用,在打开URL scheme时,会弹出对话框,询问是否用xx应用来打开。若未安装APP则会显示“safari打不开该网页,因为网址无效”。可以通过window.location.href的方式调用scheme。

其它

微信白名单机制会导致分享到微信的页面无法正常打开原生App。

注意

url scheme为应用程序提供了潜在的攻击载体, 因此需要确保所有的url参数格式正确,将操作限制为不冒数据风险的操作。例如, 不允许直接删除内容,访问与用户有关的敏感信息。
官方建议使用universal links。

universal links (apple)

universal links是标准的http或https链接, 可以同时用来唤醒app或网页呈现。
当用户未安装app时,系统会把它当做普通的http或https链接,在浏览器中打开这个链接显示网页内容。
当用户安装app时,系统会通过一个存储在我们服务器的文件(apple-app-site-association.json)来判断网站是否允许app打开这个链接。

universal link 生效条件

  1. 当在同一域名下打开universal links,系统会认为用户想继续在浏览器中浏览网页;当域名不同时, 系统会进行打开app的操作。
  2. universal links支持的域名最多只能支持到二级域名,如果你用到了三级域名,universal links 唤端不会生效。
  3. iPhone 至少 iOS 9.2 以上。至少 Xcode 7 以上。

apple-app-site-association.json

提供支持universal links 的列表。从macOS 11 和IOS 14开始, apple-app-site-association.json 的请求不再通过服务器请求,而是通过CDN分发完成。如果服务器不能在公网下访问,需要通过另外的方式绕过CDN直接连接私有域。参考链接

如下格式:

{
  "applinks": {
      "details": [
           {
             "appIDs": [ "ABCDE12345.com.example.app", "ABCDE12345.com.example.app2" ], // 一套配置适配多个 appID
             "components": [
               {
                  "#": "no_universal_links", // # 配置支持的锚点条件
                  "exclude": true,
                  "comment": "Matches any URL whose fragment equals no_universal_links and instructs the system not to open it as a universal link"
               },
               {
                  "/": "/buy/*", // / 来配置支持的 path 格式条件
                  "comment": "Matches any URL whose path starts with /buy/"
               },
               {
                  "/": "/help/website/*",
                  "exclude": true, // exclude 是排除字段,符合这个条件的 Universal Link 不生效
                  "comment": "Matches any URL whose path starts with /help/website/ and instructs the system not to open it as a universal link"
               },
               {
                  "/": "/help/*",
                  "?": { "articleNumber": "????" }, // ? 来配置支持的字段条件
                  "comment": "Matches any URL whose path starts with /help/ and which has a query item with name 'articleNumber' and a value of exactly 4 characters"
               }
             ]
           }
       ]
   },
   "webcredentials": {
      "apps": [ "ABCDE12345.com.example.app" ]
   },
    "appclips": {
        "apps": ["ABCED12345.com.example.MyApp.Clip"]
    }
}

最终解决方案

  var ue = {};

  // 客户端检测
  ue.browserInfo = {
    userAgent: navigator.userAgent.toLowerCase(),
    isAndroid: Boolean(navigator.userAgent.match(/android/ig)),
    isIos: Boolean(navigator.userAgent.match(/iphone|ipod|ipad/ig)),
  }

  // 唤醒APP
  ue.openApp = function (urls) {
    var scheme = urls.scheme;
    var universalLink = urls.universalLink;
    var downloadUrl = urls.downloadUrl;
    if (ue.browserInfo.isIos) {
      // ios 9以上universal link
      if (ue.getIosVersion() >= 9) {
        window.location.href = universalLink;
      } else {
        // ios 9以下url scheme --iframe
        ue.iframeScheme(scheme, downloadUrl);
      }
    } else if (ue.browserInfo.isAndroid) {
      // 安卓 
      window.location.href = scheme;
    } else {
      // 其它设备
      ue.iframeScheme(scheme, downloadUrl);
    }
  };

  // 获取ios地址
  ue.getIosVersion = function () {
    var iosVersion = ue.browserInfo.userAgent.match(/os\s*(\d+)/);
    return iosVersion ? (iosVersion[1] || 0) : 0;
  }

  // iframe 方式唤醒app
  ue.iframeScheme = function (scheme, downloadUrl) {
    var $iframe = document.createElement('iframe');
    var openTime = +new Date();
    var limitNum = 100;
    $iframe.src = scheme;
    $iframe.style.display = 'none';
    document.body.appendChild($iframe); // 用户安装app, 会唤醒app
    var timer = setInterval(function () {
      if (limitNum > 0) {
        limitNum--;
      } else {
        if ((new Date()) - openTime < 2200) {//加了200ms基准误差
          document.body.removeChild($iframe);
          window.location.href = downloadUrl;
        }
        clearInterval(timer);
      }
    }, 20);
  }

  ue.init = function (urls) {
    ue.openApp(urls);
  };

参考链接

  1. developer.apple.com/documentati…
  2. developer.apple.com/documentati…
  3. www.jianshu.com/p/136fd75ab…