1. 程式人生 > >踩坑之頁端喚醒nativeAPP

踩坑之頁端喚醒nativeAPP

最近一直在填坑,好久沒寫部落格了,工作終於暫時告一段落,正好趁此機會熟悉一下坑,以後繼續填

在native app綜合表現還是優於web的現在,尤其是使用者也已經習慣於使用native app,從web導流到native的需求還是比較常見,而與旺盛的需求相反的是喚醒native app的方法或者說途徑十分操蛋,可謂是遍地是坑。

寫作大綱

需求概述

在給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想要突破其攔截很難,下一步我們可能就會推動客戶端的同學,一起學習實現業界已有解決方案。

總結

寫作大綱

  1. 可以從設計層面考慮,用兩個按鈕來規避無法探測是否安裝app情況,讓使用者主動提供資訊並選擇,兩個按鈕好像也並不違背希克法則~

  2. 確認下載app,心中永遠的痛,存在問題

    • 下載延遲
    • 彈窗干擾
    • 下載連線跳轉一定程度的不可控
  3. 想要做到更好就要多和客戶端的同學溝通,共同推進尋求更好更安全的方式跳過微信攔截,相容多種情況。

  4. 當然具體專案具體分析,如果對安全性等要求不高,可以考慮第三方,如果活動或者頁面針對性比較強,可以多和運營產品溝通,儘量做好做快。

下面給出一些個人覺得不錯的參考資料