从同构的角度和降级支持的角度来看,使用Android和iOS系统增强的AppLinksUniversal Links,通过HTTPS统一标准打开APP是标准的选择。

使用自定义Scheme打开APP适用于:

  1. 网站尚不支持HTTPS;
  2. App的iOS版本尚未添加Universal Links支持;
  3. APP的Android版本尚未添加AppLinks支持,同时需要支持Android 6.0以下版本。

使用自定义Scheme打开App的难点在于浏览器的兼容性问题和alert弹窗的提示干扰问题。 在iOS和Android中可以用location.href请求自定义Scheme可以打开App,用iframe只能在Android中打开,在iOS中无法打开。故下面的实现用location.href请求的方案。

DEEP-LINK.JS基础上修改为applink,放在团队代码库下。 将原来DeepLink的方案扩展为AppLink,使其兼容常见下载的需求。

基本思路

  1. 自动跳转链接赋值给href属性作为下载的默认值;
  2. 将AppStore地址、Android下载包地址、应用宝推广链接和应用deeplink地址赋值给自定义属性供程序使用;
  3. 将下载链接进行优化,避免302跳转带来的性能损失;
  4. 监听a链接点击事件,当存在deeplink时,尝试打开App,打开失败后进行下载或AppStore跳转(微信内部跳转到应用宝);
  5. visibility发生变化后,取消备用方案执行。

核心代码

<a 
    href="download-link"                         // 服务器端设置的根据UA判断的自动跳转链接包含下面4中情况
    data-href-android="android-download-link"    // Android包下载地址
    data-href-ios="itunes-link"                  // itunes地址
    data-href-qq-android="myapp-link"            // 应用宝推广地址
    data-href-qq-ios="myapp-link"                // 应用宝推广地址
    data-deeplink="deep-link"                    // 跳转App地址
>立即打开</a>
// 下载链接优化代码
// 根据agent取指定的值进行优化赋值
function optimize(anchor) {
    if (!anchor) {
        return;
    }

    var href = anchor.getAttribute('data-href-' + agent);

    if (!href) {
        return;
    }

    anchor.href = href;
}
// 尝试打开App代码
// App打开之后,浏览器转入后台运行,计时器会暂停,根据这个时间差判断是否打开了App
// 如果App打开失败,则执行后备方案,跳转到href地址进行App下载活安装
document.body.addEventListener('click', function (e) {
    // Hijack click event

    var target = e.target,
        deeplink = target.getAttribute('data-deeplink'),
        start;

    if (target.tagName.toLowerCase() !== 'a' || !deeplink || clicked || timeout) {
        return;
    }

    e.preventDefault();
    e.stopImmediatePropagation();

    // Store start time
    start = Date.now();
    clicked = true;

    // Timeout to detect if the link worked
    timeout = setTimeout(function () {
        // Check if any of the values are unset
        if (!clicked || !timeout) {
            return;
        }

        // Reset things
        clicked = false;
        timeout = null;

        // Has the user left the screen? ABORT!
        if (Date.now() - start >= delay * 2) {
            return;
        }

        open(target.href);
    }, delay);

    // Go to app
    open(deeplink);
}, false);
// 取消备用代码执行
// 当浏览器窗口发生visibility变化时,App应该被调起过
function handleVisibilityChange() {
    // Triggered on blur
    if (!clicked || !timeout) {
        return;
    }

    // Reset everything
    clearInterval(timeout);
    timeout = null;
    clicked = false;
}

document.addEventListener(visibilityChange, handleVisibilityChange, false);

测试结果

任何技术方案和要交付的工作,必须做足够多的测试,因为我们要为自己的产出负责,要为下游的测试同事负责——而且自我再多的测试也是不足够充分的,总有一些小漏洞在暗处等着你我,出现问题解决问题这是我们的态度。

已安装启动测试结果

系统版本\浏览器 系统浏览器 UC浏览器 百度浏览器 QQ浏览器 猎豹浏览器
iOS 8.4.1 直接启动 ActionSheet确认打开AppStore,返回后触发ActionSheet确认打开AppStore 直接启动 直接启动,返回后打开应用宝App页面 直接启动,返回后浏览器内部打开AppStore
iOS 9.3.5 弹窗确认 直接启动 直接启动 直接启动,返回后打开应用宝App页面 直接启动
iOS 10.1.1 弹窗确认 ActionSheet确认 直接启动 直接启动,返回后打开应用宝App页面 直接启动,返回后浏览器内部打开AppStore
Z2 Android 4.4.2 直接启动 ActionSheet确认,返回后触发下载 无法启动,触发下载 弹窗确认,返回后触发下载 弹窗确认
MX5 Android 5.0.1 直接启动,触发下载 直接启动,触发下载 无法启动,触发下载 弹窗确认,返回后触发下载 弹窗确认
X6 Android 5.0.2 ActionSheet确认,返回后触发下载 ActionSheet确认,返回后触发下载 无法启动,触发下载 弹窗确认,返回后触发下载 弹窗确认,返回后触发下载
T2 Android 5.1.1 直接启动 ActionSheet确认 无法启动,触发下载 弹窗确认,返回后触发下载 直接启动
Mi5 Android 6.0.1 直接启动 ActionSheet确认,返回后触发下载 无法启动,触发下载 弹窗确认,返回后触发下载 弹窗确认,返回后触发下载
6P Android 7.0.0 直接启动 ActionSheet确认,返回后触发下载 无法启动,触发下载 弹窗确认 弹窗确认,返回后触发下载

未安装下载测试结果

系统版本\浏览器 系统浏览器 UC浏览器 百度浏览器 QQ浏览器 猎豹浏览器
iOS 8.4.1 先显示链接错误弹窗,再AppStore打开确认 先显示链接错误弹窗,之后ActionSheet提醒打开AppStore 直接打开AppStore 打开应用宝App页面 提示应用未安装之后在浏览器内部打开AppStore
iOS 9.3.5 先显示链接错误弹窗,再AppStore打开确认 ActionSheet提醒打开AppStore 直接打开AppStore 打开应用宝App页面 提示应用未安装之后打开AppStore
iOS 10.1.1 先显示链接错误弹窗,再AppStore打开确认 直接打开AppStore 直接打开AppStore 打开应用宝App页面 浏览器内部打开AppStore
Z2 Android 4.4.2 打开自定义scheme页面 ActionSheet提示打开App,关闭ActionSheet后开始下载 直接下载 直接下载 直接下载
MX5 Android 5.0.1 直接下载 直接下载 直接下载 直接下载 直接下载
X6 Android 5.0.2 直接下载 ActionSheet提示打开App,关闭ActionSheet后开始下载 直接下载 直接下载 直接下载
T2 Android 5.1.1 直接下载 直接下载 直接下载 直接下载 直接下载
Mi5 Android 6.0.1 直接下载 ActionSheet提示打开App,关闭ActionSheet后开始下载 直接下载 直接下载 直接下载
6P Android 7.0.0 选择下载器下载 ActionSheet提示打开App,关闭ActionSheet后开始下载 直接下载 直接下载 直接下载

参考资料

  1. 移动DeepLink的前生今世;
  2. DEEP-LINK.JS
  3. DEEPLINK 与增长黑客:低成本实现用户增长的方法论
  4. Deep Link是什么
  5. How to Deep Link to Your Mobile App from Your Website
  6. Deferred Deep Linking in iOS
  7. 支持IOS9+和Android5+,JS打开APP的解决方案
  8. 怎么在网页中打开你的app
  9. IOS9通用链接(universal link)