踩坑之页端唤醒nativeAPP

最近一直在填坑,好久没写博客了,工作终于暂时告一段落,正好趁此机会熟悉一下坑,以后继续填

在native app综合表现还是优于web的现在,尤其是用户也已经习惯于使用native app,从web导流到native的需求还是比较常见,而与旺盛的需求相反的是唤醒native app的方法或者说途径十分操蛋,可谓是遍地是坑。

踩坑之页端唤醒nativeAPP_第1张图片

需求概述

在给app导流中,为了提高转化率,就需要让用户不管是在微信或者是浏览器中,都能在点击链接后, 唤起本地的 app 后 , 跳转到指定页面 。

虽然这个功能从用户体验方面来说是自然而然的,但是由于 iOS/Android 平台差异性,在实现过程中还是有些问题。

实现思路

首先梳理一下整个大的流程的三步:

  1. web提供给用户唤醒app的UI元素
  2. 用户点击后唤醒app
  3. 唤醒app后app的行为

第一步总体来说问题不大,当然这一步某种意义上是最重要的,因为你面临一个根本性的方案选择。

  1. 只给用户提供一个打开的UI元素
  2. 提供给用户下载和打开两个UI元素

显然我们日常生活中大多看到的是第一种方式,据说知乎是第二种方式,我们将就第一种方式展开讨论一下。

第二步是整个过程中的重中之重,其实又可以细分成下面三块:

  • 监听到用户的操作事件后确认是否下载app
  • 若没下载则跳转到下载链接
  • 若下载了则唤醒app

这个逻辑看似简单,然而。。。

第三步则一般都是预先设定好的跳转链接。

开始踩坑

我们假设提供一个打开按钮,并绑定click事件,设置好click的回调函数

这时我们就面临着两个难题:

  1. 如何判断浏览器是否安装了对应app
  2. 如何唤起本地app

判断是否安装app

浏览器无法直接明确判断是否安装app,所以我们只能采用蹩脚的取巧办法,而这也带来了一系列后续问题

取巧的思路很简答,就是等,既然已经触发链接了,那么只有两种结果,唤起app和没唤起,假设没唤起app的情况全是未安装app,那么等待一段时间后就跳转链接。所以我们要做的就是:

  1. 决定一个deadline,也就是默认未安装app的时间
  2. 如何更快更好更准的判断是否唤醒

deadline没什么办法,只能根据自己的实际情况进行试验,其实主要考虑的也就是用户体验以及用户设备反应或者说卡顿程度等

第二个问题就略微复杂了,唤醒app后,app将浏览器覆盖,这里有几个属性和事件可以基本应付大部分场景

pagehide: 页面隐藏时触发

visibilitychange: 页面隐藏没有在当前显示时触发,比如切换tab,也会触发该事件

document.hidden 当页面隐藏时,该值为true,显示时为false

给出简单的实现:

        var hidden;
        var visibilityChange;
        if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support
            hidden = 'hidden';
            visibilityChange = 'visibilitychange';
        } else if (typeof document.msHidden !== 'undefined') {
            hidden = 'msHidden';
            visibilityChange = 'msvisibilitychange';
        } else if (typeof document.webkitHidden !== 'undefined') {
            hidden = 'webkitHidden';
            visibilityChange = 'webkitvisibilitychange';
        }

if (hidden) {
                    window.addEventListener('pagehide', removeEvent, false);
                    document.addEventListener(visibilityChange, removeEvent, false);
                }

鉴于其实pagehide和visibilityChange的兼容性情况没那么好,所以还加了个ployfill,同时也借此实现deadline。

简略代码如下:

        function interval(func, wait, times, callback) {
            var interv = (function (w, t) {
                var intervalTimes = t;
                return function () {
                    if (typeof t === 'undefined' || intervalTimes > 0) {
                        try {
                            if (func.call(null)) {
                                return;
                            }
                        } catch (e) {
                            intervalTimes = 0;
                            throw e.toString();
                        }
                        setTimeout(interv, w);
                    } else {
                        callback.call(null);
                    }
                    intervalTimes -= 1;
                };
            }(wait, times));
            setTimeout(interv, wait);
        }

            var startTime = Date.now();
            var delayTime = 1000;
            interval(function () {
                if (document[hidden] || Date.now() - startTime > 2000 + delayTime) {
                    removeEvent();
                    return true;
                }
                return false;
            }, 20, 100, function () {
                reject(new Error('FAILWAKE'));
            });

看着很多行,其实只做了一件事,每20毫秒就判断一下document的hidden属性和定时器执行时间是否超过计划时间 + delaytime。

关于delaytime这一点,其实是利用了唤起app后,浏览器进入后台,计时器线程被阻塞的特性,所以执行时间减缓来判断是否浏览器是否进入后台,即是否唤醒app。

但是这样其实并没有完全解决问题,假设用户的设备没那么好,总是在唤醒前不久跳转downlink,也包括在唤醒后不久如果事件或者计时器没能触发,那么还是会跳转downlink,用户在退出app后看到的就是下载提示,另外还有些浏览器会有弹窗提示,综合来说用户体验就有点差了。

如何唤起本地app

好了,勉强解决了第一个难题,下面就是核心坑,如何唤醒app

首先大概有三种可以选择的跳转方式:

1.a标签的href属性。

var a = document.createElement('a');
a.href = link;
a.style.display = 'none';
document.body.appendChild(a);

2.location对象的href属性

window.location.href = link;

3.iframe标签的src属性

var iframe = document.createElement('iframe');
iframe.src = deeplink;
iframe.style.display = 'none';
iframe.style.width = 0;
iframe.style.height = 0;
iframe.style.border = 0;

document.body.appendChild(iframe);

这三种方式在两大平台各大浏览器、webview兼容性参差不齐,详细的可以参考愚人码头的这篇文章。

link的方案是唤醒app方案的核心,也是坑最多的地方

1.URL Scheme
URL Scheme是iOS,Android平台都支持,只需要原生APP开发时注册scheme, 那么用户点击到此类链接时,会自动唤醒APP,借助于URL Router机制,则还可以跳转至指定页面。这种方式是当期使用最广泛,也是最简单的,但是需要手机,APP支持URL Scheme。

2.Universal Links
在2015年的WWDC大会上,Apple推出了iOS 9的一个功能:Universal Links通用链接。如果你的应用支持Universal Links(通用链接),那么就能够方便的通过传统的HTTP链接来启动APP(如果iOS设备上已经安装了你的app,不需要额外做任何判断等), 或者打开网页(iOS设备上没有安装你的app).或许可以更简单点来说明,在iOS9之前,对于从各种从浏览器,Safari、UIWebView或者 WKWebView中唤醒APP的需求,我们通常只能使用scheme。

3.App Link
使用 app links,当点击了链接,安卓系统会检查是否有一个 app 可以处理 url(比如 twitter.com),然后跟核对哪个app(s)可以处理该域名的链接,直接在应用内处理,这样我们就能避免弹框影响用户。

主要来说可能是这三个方案使用比较多一点,具体代码就不贴了,主要还是可能推动配合客户端的同学。

另外还有一个深坑就是微信的拦截,微信用户月活恐怖,流量的重要来源,除了微信自己白名单,其他app想要突破其拦截很难,下一步我们可能就会推动客户端的同学,一起学习实现业界已有解决方案。

总结

踩坑之页端唤醒nativeAPP_第2张图片

  1. 可以从设计层面考虑,用两个按钮来规避无法探测是否安装app情况,让用户主动提供信息并选择,两个按钮好像也并不违背希克法则~

  2. 确认下载app,心中永远的痛,存在问题

    • 下载延迟
    • 弹窗干扰
    • 下载连接跳转一定程度的不可控
  3. 唤醒app的方式五花八门,最好还是自己多加尝试,当然这其实是很傻的,因为可以用第三方啊~
    • 磨窗 http://www.magicwindow.cn/
    • deepshare http://deepshare.io/redirect-once/
  4. 想要做到更好就要多和客户端的同学沟通,共同推进寻求更好更安全的方式跳过微信拦截,兼容多种情况。

  5. 当然具体项目具体分析,如果对安全性等要求不高,可以考虑第三方,如果活动或者页面针对性比较强,可以多和运营产品沟通,尽量做好做快。

下面给出一些个人觉得不错的参考资料

  1. 浏览器中唤起native app || 跳转到应用商城下载(一)
  2. iOS/Android 微信及浏览器中唤起本地 APP
  3. 从浏览器或者Webview 中唤醒APP
  4. h5唤起app
  5. iOS Universal Links(通用链接)
  6. 移动DeepLink的前生今世
  7. https://github.com/prabeengiri/DeepLinkingToNativeApp/blob/c6732738af34191f5943199e39adda588b8365f6/deeplink-to-native-app.js
  8. https://github.com/AlanZhang001/H5CallUpNative/blob/master/tool-nativeSchema.js

你可能感兴趣的:(JavaScript)