H5 喚醒 APP 解決方案

參考:https://github.com/bsxz0604/R...javascript

核心代碼

// dom
<a id="openApp" href="javascript:;" title="">當即打開</a>

// js
;(function (window) {
    var ue = {};

    // 客戶端檢測
    ue.browserInfo = function () {
        var json = {
            userAgent: navigator.userAgent.toLowerCase(),
            isAndroid: Boolean(navigator.userAgent.match(/android/ig)),
            isIos: Boolean(navigator.userAgent.match(/iphone|ipod|ipad/ig)),
            isWeChat: Boolean(navigator.userAgent.match(/MicroMessenger/ig)),
            isQQ: Boolean(navigator.userAgent.match(/ QQ/ig)),
            isQQBrowser: Boolean(navigator.userAgent.match(/MQQBrowser/ig))
        }

        return json;
    }

    // 喚醒APP
    ue.openApp = function () {
        var $btnOpenIos = $('#downloadIos');
        var browser = ue.browserInfo();
        var scheme = '';
        var iosVersion = browser.userAgent.match(/os\s*(\d+)/);
        var universalLink = 'a';

        iosVersion = iosVersion ? (iosVersion[1] || 0) : 0;

        if (browser.isAndroid) { // 安卓
            scheme = 'b';
        } else if (browser.isIos) { // ios
            scheme = 'c';
        }

        $btnOpenIos.on('click', function () {
            //客戶端檢測微信
            if (browser.isWeChat) {
                $('.pop-openbrowser').show();
            } else {
                if (browser.isAndroid) {
                    var ifr = document.createElement('iframe');
                    ifr.src = scheme;
                    ifr.style.display = 'none';
                    document.body.appendChild(ifr);

                    setTimeout(function () {
                        document.body.removeChild(ifr);
                    }, 2000);
                } else {
                    // ios9+
                    if (iosVersion >= 9) {
                        document.location.href = universalLink;
                    } else {
                        setTimeout(function () { // 必需要使用settimeout
                            var a = document.createElement("a"); //建立a元素
                            a.setAttribute("href", scheme), a.style.display = "none", document.body.appendChild(a);
                            var t = document.createEvent("HTMLEvents"); // 返回新建立的 Event 對象,具備指定的類型。
                            t.initEvent("click", !1, !1) // 初始化新事件對象的屬性
                            a.dispatchEvent(t) // 綁定事件
                        }, 0)
                    }
                }

                var checkOpen = function (cb) {
                    var _clickTime = +(new Date());

                    function check(elsTime) {
                        if (elsTime > 3000 || document.hidden || document.webkitHidden) {
                            cb(1);
                        } else {
                            cb(0);
                        }
                    }
                    //啓動間隔20ms運行的定時器,並檢測累計消耗時間是否超過3000ms,超過則結束
                    var _count = 0,
                        intHandle;
                    intHandle = setInterval(function () {
                        _count++;
                        var elsTime = +(new Date()) - _clickTime;
                        if (_count >= 100 || elsTime > 3000) {
                            clearInterval(intHandle);
                            check(elsTime);
                        }
                    }, 20);
                }
                checkOpen(function (opened) {
                    // APP沒有打開成功  而且開啓自動跳轉到下載頁
                    if (opened === 0) {
                        location.href = universalLink
                    }
                });
            }
            ue.trackEventDownload();
        });
    };

    ue.init = function () {
        ue.openApp();
    };
})(window);
$(function () {
    ue.init();
})

業務邏輯

概念敘述

調起APP在不一樣平臺用不一樣的方式,主要就分3個html

* URI Scheme
* universal Link
* Android App Links

如今仍是有不少第三方(例如魔窗)來協助你處理這個事情,經過接入他們的SDK和客戶端代碼來處理,可是萬變不離其宗,全部的第三方也離不開這3種方式。前端

  1. URI Scheme:java

    • URI Scheme 是iOS,Android平臺都支持,只須要原生APP開發時註冊 scheme , 用戶點擊到此類連接時,會自動喚醒APP,藉助於 URL Router 機制,則還能夠跳轉至指定頁面。
    • <scheme name> : <hierarchical part> [ ? <query> ] [ # <fragment> ]android

      • <scheme name>:是scheme的名稱,表明着協議名稱。
      • <hierarchical part>:它包含 authority 和 path。
      • <query>:可選項目,隔開或&隔開的鍵值對<key>=<value>
      • <fragmentg> :可選項目包,其它額外的標識信息

例如:ios

git://github.com/user/project-name.git
ftp://user1:1234@地址
musically://musical?id=xxxx&key=xxxx
  1. universal Link:git

    • iOS9 後推出的一項功能,通用連接,對於前端即訪問一個https的url,若是這個url帶有你提交給開發平臺的配置文件中匹配規則的內容,iOS系統會去嘗試打開你的app,若是打不開,系統就會在瀏覽器中轉向你要訪問的連接。
    • universal Link 工做方式以下:github

      1. 訪問web link
      2. iOS訪問 https://xxxxxxx/apple-app-sit... 並解析,獲取文件中的信息(App的Team ID和Bundle ID)
      3. 經過Bundle ID 檢查本地是否存在對應app,和檢查PATH信息等,若是有app打開app,若是沒有則跳轉對應web link(可經過代碼實現跳去app Stroe)
  2. Android App Links:web

    • 在2015年的Google I/O大會上,Android M宣佈了一個新特性:App Links讓用戶在點擊一個普通web連接的時候能夠打開指定APP的指定頁面,前提是這個APP已經安裝而且通過了驗證,不然會顯示一個打開確認選項的彈出框,只支持Android M以上系統。
    • 簡單的說就是創建APP和某個連接的關聯,避免系統在處理該類型連接時彈出選擇框。彈框最多見的就是瀏覽器打開時的選擇框。彈出選擇框是應用註冊了相應scheme,applinks的做用是避免在打開本身域名的連接時彈出選擇(前提是註冊了相應scheme),能夠實現直接打開本身關聯的app。

對比/優劣

1. URI Scheme

URI Scheme的兼容性是最高,在使用的過程當中,會發現有不少限制:json

  • 當要被喚起的app沒有安裝時,這個連接就會出錯。在國內很是雜亂的瀏覽器中,會出錯的現象會不少種類型。
  • 當註冊有多個scheme相同的時候,目前沒有辦法區分。
  • 不支持從其餘app中的UIWebView中跳轉到目標app

也就由於會有這些緣由,apple和android都出現了本身第二套解決方案。

2. universal Link

從連接上看來,是一個web link,因此也就解決了當沒有app時,跳轉也不會出現報錯,因此相對Scheme優點就提現出來了。

  • 當已經安裝app,不須要加載任何web頁面,app就會當即啓動;app沒有安裝,就會跳去對應的web link。
  • universal Link 是從服務器上查詢是哪一個app須要被打開,因此不會存在衝突問題
  • universal Link 支持從其餘app中的UIWebView中跳轉到目標app
  • 隱私性,提供universal Link給別的app進行app間的交流,然而對方並不可以用這個方法去檢測你的app是否被安裝。

固然universal Link也不是十全十美的,缺陷也是存在的:

  • 會記住用戶的選擇:在用戶點擊了Universal link以後,iOS會去檢測用戶最近一次是選擇了直接打開app仍是打開網站。一旦用戶點擊了這個選項,他就會經過safiri打開你的網站。而且在以後的操做中,默認一直延續這個選擇,除非用戶從你的webpage上經過點擊Smart App Banner上的OPEN按鈕來打開。

3. app link 和 universal Link

差別不大。也是爲了更好的提供調起app出現的google的方案。優勢與 universal Link 差很少,缺點主要以下:

* 國內的支持相對較差,在有的瀏覽器或者手機ROM中並不能連接至APP,而是在瀏覽器中打開了對應的連接。 
* 在詢問是否用APP打開對應的連接時,若是選擇了「取消」而且「記住選擇」被勾上,那麼下次你再次想連接至APP時就不會有任何反應

不管哪種方式目前都沒有解決幾個問題:

  • 若是設備上沒有安裝這個app的時候,安裝完畢後,沒法保留住此時用戶停留的上下文。
  • 由於web沒有辦法監聽到APP是否安裝,因此都須要經過一些手段來兼容調起app或者是去下載頁

使用 & 須要注意的內容

  1. URI Scheme:

    • 使用: 這種方式是當期使用最普遍,也是最簡單的,可是須要 APP 支持 URI Scheme 。
    • 須要注意的內容 & 遇到的問題:
其實使用URI Scheme 部分前端沒有太多能夠排查的問題,會遇到的問題主要是兩個部分。1. 在android的兼容性處理(國內的瀏覽器無力吐槽ing),2. 當沒有安裝app的狀況,URI Scheme 會有各類報錯,也須要處理…
  1. universal Link & app Links

    • 使用:對於有app的用戶,只是打開一個鏈接,可是須要注意的是須要考慮到沒有APP的用戶。(我的的解決方案:針對域名來判斷,當域名爲特定的universal Link 的域名,則跳轉去下載頁面)
    • 須要注意的內容 & 遇到的問題:

      1. apple-app-site-association 和 assetlinks.json 的配置
      2. 須要保證使用的連接跨域(universal Link)
      3. 直接將universal Link 貼入瀏覽器的url中不會生效
      4. window.onload 或者用戶沒有任何事件觸發的狀況下,universal Link也不會生效

兩大平臺的特殊處理(facebook & twitter)

facebook 和 twitter 做爲國外的兩大信息聚合平臺,對於在他們app中調起app也有本身的一套方式。
根據要求經過添加META頭來處理打開APP
facebook:

<meta property="fb:app_id" content="xxxxxx" />
<meta property="og:type" content="xxxx"/>
<meta property="og:title" content="xxx" />
<meta property="al:ios:url" content="{{ uri scheme }}" />
<meta property="al:android:url" content="{{ uri scheme }}" />
<meta property="al:ios:app_store_id" content="{{app_store_id}}" />
<meta property="al:ios:app_name" content="{{xxx}}" />
<meta property="al:android:app_name" content="{{xxx}}" />
<meta property="al:android:package" content="{{android:package}}" />

twitter:

<meta name="twitter:card" content="app" />
<meta name="twitter:site" content="xxxxx" />
<meta name="twitter:title" content="xxxxx" />
<meta name="twitter:description" content="xxxxxxx" />
<meta name="twitter:image" content="xxxx" />
<meta name="twitter:app:name:iphone" content="xxx">
<meta name="twitter:app:id:iphone" content="xxx">
<meta name="twitter:app:url:iphone" content="{{Scheme}}">
<meta name="twitter:app:name:ipad" content="xxx">
<meta name="twitter:app:id:ipad" content="xxx">
<meta name="twitter:app:url:ipad" content="{{Scheme}}">
<meta name="twitter:app:name:googleplay" content="xxx">
<meta name="twitter:app:id:googleplay" content="xxx">
<meta name="twitter:app:url:googleplay" content="{{Scheme}}">

第三方服務

從以上內容能夠總結出:要作一個兼容性很好的方案,就須要考慮各類狀況,在不一樣的狀況適配不一樣的方案,比方說用戶是在手機瀏覽器打開仍是微信中打開,或者是在pc中打開,universal link是否被關閉等,這就使代碼實現變得複雜,且容易出錯,且還有安卓平臺機型衆多、瀏覽器衆多等致使的兼容問題。

若是以爲實現難度或者成本過高,你能夠考慮使用第三方提供的服務,例如魔窗的`mLink。具體請查看官網,在此不贅述。

使用 checkList(前端)

  1. scheme

    • iOS 和 android 是否已經支持 此scheme
    • js處理兼容代碼
  2. universal Link (apple-app-site-association 官方文檔)

    • HTTPS的域名
    • iOS9 以上
    • universal Link 是否跨域
    • universal Link的落地頁是不是下載頁面
    • apple-app-site-association 配置在 host的根目錄和.well-known下

    • apple-app-site-association會在第一次打開app或者更新app時候會去拉去,因此確認是否更新了apple-app-site-association後沒有更新過app
    • 檢查apple-app-site-association paths 大小寫敏感 支持通配符
    • 該設備的用戶選擇了直接打開app仍是打開網站,若是選擇打開網站,須要經過smart banner 從新啓用
    • 跳轉處理是不是在用戶事件中觸發,而不是進入頁面後直接觸發
  3. app links (android app links官方文檔)

    • HTTPS的域名
    • 跳轉後的落地頁是不是下載頁面
    • assetlinks.json 配置在 host的.well-known下

  4. facebook (facebook app link官方文檔)

    • 將須要的meta頭信息填充完畢
    • 檢測連接 分享調試器 - Facebook for Developers , 確認分享連接中獲取到了所須要的meta頭
    • 分享過的連接會有緩存,在檢測中清楚緩存
    • 若是web和wap連接一致,確認在web中也添加了相同的meta頭,facebook會默認從web中獲取
  5. twitter (Twitter app card官方文檔)

    • 將須要的meta頭信息填充完畢
    • 檢測連接 Twitter app card 檢測
    • 若是web和wap連接一致,確認在web中也添加了相同的meta頭,facebook會默認從web中獲取

可能出現的 bug

1. iOS 11.2 universal link 失效的 bug:

https://blog.branch.io/notice...

失效現象:
沒法直接喚醒app,而是直接跳轉到appStore,用戶再點擊【打開】。

解決方案:
iOS 開發能夠配置通用連接讓其【強制打開】,可是可能出現該錯誤提示框,這樣給用戶的體驗反而更很差。

2. QQ瀏覽器、微信已屏蔽 scheme & universal link

騰訊系封殺了除本廠及白名單 app 以外的全部 app 的喚醒功能。

參考資料連接

  1. webview如何屏蔽universal Link
  2. apple-app-site-association 官方文檔
  3. apple-app-site-association 檢測
  4. android app links官方文檔
  5. android app links檢測
  6. facebook app link官方文檔
  7. 分享調試器 - Facebook for Developers
  8. Twitter app card官方文檔
  9. Twitter app card 檢測
相關文章
相關標籤/搜索