通过js唤醒app或者跳转APP Store下载

3,817 阅读3分钟

需求:

  • 若安装app,拉起app
  • 未安装app,跳转App Store

背景

  • 普通浏览器兼容,微信有权限限制,只能每次跳转下载页
  • 前提我们首先知道js无法判断出当前手机里面App是否安装。(假如H5页面在微信内打开,可通过调用微信内部的JS API判断出App是否安装,但是该api并不对外开放)。浏览器中打开H5页面唤起App时也是无法判断出当前app是否安装。
  • 拉起app的原理就是js和原生统一好一个地址(ex: xxxx://self/xxx.xxxxpage),然后浏览器发起请求,如果安装了app则会拦截这个请求并且打开。

实现方案

针对这种背景情况 ,目前业内普遍采取两种做法:

  1. 是利用延时器原理,打开H5页面会尝试唤起App,假如失败了几秒后会直接跳转到下载页面 (缺点是即使用户安装了App过了时间也会跳转到下载页,从App返回到浏览器时也会变成下载页,体验很不好)但是有方案可以解决

  2. 不采用延时器,直接跳转到App下载页面。

实现

原生侧配置:scheme和host要和js这边保持统一

js侧如下

/* 写法一: */
window.location.href="app的scheme" 
setTimeout(function() {
    window.location.href="下载地址" 
}, 500);

但是以上代码明显有两处问题:

  1. 单说拉起app,通过a链接或者window.location.href=”xxx”即可,但是某些手机在未安装app时会识别我们上面定义的跳转协议尝试打开这个网页,此时会报找不到网页。这里用iframe的方式兼容性更好。
  2. 在未安装app时应跳转下载页,写法一直接一个延时跳转一下就行,但拉起app并不会自动清除定时器,即无论是否安装了app,页面在500ms后都会跳转到下载页。

方案一:是用iframe跳转,设置一个初始时间,打开拉起app地址后有个结束时间,计算两者之差,当差大于一定时间时说明安装了app,当差很小是说明没有打开app,此时需要跳转到下载页。

出于节能的考虑, 部分浏览器在进入后台时(或者失去焦点时), 会将 setTimeout 等定时任务暂停待用户回到浏览器时, 才会重新激活定时任务。说是暂停, 其实应该说是延迟, 1s 的任务延迟到 2s, 2s 的延迟到 5s, 实际情况因浏览器而异。

代码如下

/* 优化版的写法一: */ 
downloadButton.addEventListener('click', function() { 
    var startTime = new Date();//记录初始时间 
    var t = 500; 
    var ifr = document.createElement('iframe'); 
    ifr.src = 'app的scheme';//打开app 
    document.body.appendChild(ifr); 
    ifr.onload = function() { 
        ifr.style.display='none'; 
        setTimeout(function() { 
            document.body.removeChild(ifr); 
            var endTime = new Date();//记录结束时间 
            if (endTime - startTime <= (t + 30)) {
                //两者之差小于30ms时跳转到下载页 
                window.location.href = "下载地址" 
            } 
        }, t); 
    }; 
})
/*
    写法二:
*/
const appDownload = () => { 
    function failed() {
       window.location.href = '应用商店的地址'
    }


  function transfer(cb) {	
    window.location.href = 'app的scheme';

    const initialTime = new Date();
    let counter = 0;
    let waitTime = 0;
    const checkOpen = setInterval(() => {
      counter++;
      waitTime = new Date() - initialTime;
      if (waitTime > 3500) {
        clearInterval(checkOpen);
        cb();
      }
      if (counter < 1000) {
        return;
      };
    }, 20);
    
    document.addEventListener('visibilitychange', () => {
      const isHidden = document.hidden;
      if (isHidden) {
        clearInterval(checkOpen);
      }
    });
  }

  transfer(failed);
}

// 如果判断在微信中提示复制链接到浏览器中打开
if(!this.isWeixin()) {
	appDownload();
}