在移動互聯網,鏈接是比較重要的傳播媒質,但很多時候我們又希望用戶能夠回到APP中,這就要求APP可以通過瀏覽器或在微信中被方便地喚起。
這是一個既直觀又很好的用戶體驗,但在實現過程中會遇到各種問題:
- 如何解決未安裝APP時的做好引導頁
- 如何在微信中喚醒APP
- 在iOS9中如何處理universal link被用戶誤關的情況
- 如何解決Android各種機型、各種第三方瀏覽器導致的兼容問題等
- 在APP未安裝情況下,引導用戶下載后打開APP后,如何進入之前喚起時指定的頁面或內容,即如何實現場景還原
- 在微信中喚醒APP時,如何進入指定的頁面或內容
下面是我一些個人的經驗分享。
瀏覽器中打開
iOS/Android APP配置
這塊內容其實比較簡單,在網上都有很多資料可供查閱,就不再贅述。
原理說明
首先需要說明,不管iOS還是Android,瀏覽器都不可能預知本地是否安裝了某個APP的。或者更嚴謹地說,我們不能通過瀏覽器來預知本地是否安裝。因為就算瀏覽器可以讀取本地應用的安裝列表,但是目前也沒任何一家瀏覽器提供查詢的API,所以這條路是走不通的。
本質上瀏覽器是通過URL scheme打開APP,一個APP可以設置一個或多個打開自己的URL scheme。比如,Twitter就注冊自己能被「twitter://」打開。
其實,如果是做APP間相互跳轉是比較簡單的。iOS就可以使用 UIApplication 的 canOpenUrl 方法來檢測URL scheme 是否能打開對應的APP。比如,如果「twitter://」檢測能被打開,也就說明本地安裝了 Twitter 。再用 UIApplication 的 openURL 方法,就能打開Twitter了。Android 中的做法類似。
實現方案
因為iOS9和之前的iOS系統有區別,所以這里我們也要區別對待。
iOS7/iOS8
iOS中默認通過Safari打開URL scheme,方法一般如下兩種:
- 直跳方式:點擊鏈接、修改 window.location 等。
- iframe 方式:在 body 上添加 iframe,設置src屬性為跳轉的URL scheme。
第一種情況:
<a href="schemeUrl">喚醒你的APP</a>
或者
window.location.href = schemeUrl;
但在第一種情況,如果APP喚醒失敗,或者APP未安裝的話,很多時候都會跳到錯誤頁,這很影響用戶體驗,而我們的要求可能是跳轉到其他頁面或者下載APP。
后一種方法不會引起頁面可見的變化(例如頁面內容變成一個新頁面),不會導致瀏覽器歷史記錄的變化,大致實現如下:
<a href="APP下載地址">下載或打開APP</a> <script> $('a').click(function() { var ifr = document.createElement('iframe'); ifr.src = '自定義 URL scheme'; ifr.style.display = 'none'; document.body.appendChild(ifr); setTimeout(function(){ document.body.removeChild(ifr); }, 3000); }); </script>
過程是這樣:點擊 a 標簽時,首先會嘗試打開URL scheme,如果成功,就喚起APP;如果失敗,則跳轉到 href 屬性,即下載頁。
Android
但這個方案在很多安卓機型上有問題,為保證可用,改用第一種方案:
$('a').click(function() { location.href = '自定義 URL scheme'; t = Date.now(); setTimeout(function(){ if (Date.now() - t < 1200) { location.href = 'Android 下載地址'; } }, 1000); return false; }
理想過程是這樣:瀏覽器嘗試打開 URL scheme,在1秒計時后,檢查當前時間,如果實際時間已過 1200 毫秒,說明喚起APP 成功(喚起 APP 會讓瀏覽器的定時器變慢);如果沒超過 1200 毫秒,很可能是沒有安裝應用,就跳到下載地址。
或者換種方式:
var ifr = document.createElement('iframe'); ifr.src = 'com.baidu.tieba://'; ifr.style.display = 'none'; document.body.appendChild(ifr); var openTime = +new Date(); window.setTimeout(function(){ document.body.removeChild(ifr); if( (+new Date()) - openTime > 2500 ){ window.location = 'http://exam.com/xxxx.apk'; } },2000)
但原理都是一樣,利用setTimeout。但這其實不穩定,因為Android是基於Linux的分時多任務的,setTimeout的基准偏差可能會沒那么大。
但如果設置比較小的運行間隔(<30ms),在瀏覽器或者webview中,應用切換到后台,setInterval
會被很明顯的延遲執行,比如設置一個運行間隔20ms,總計運行100次的定時器,如果頁面一直處於前台,則100次跑完,總耗時與 100x20=2000ms不會有太大差異,但頁面在后台運行時,此時間會明顯超過2000ms。可以利用這一點來實現是否成功打開APP檢測及回調。
function openApp(openUrl, appUrl, action, callback) { //檢查app是否打開 function checkOpen(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); } //在iframe 中打開APP var ifr = document.createElement('iframe'); ifr.src = openUrl; ifr.style.display = 'none'; if (callback) { checkOpen(function(opened){ callback && callback(opened); }); } document.body.appendChild(ifr); setTimeout(function() { document.body.removeChild(ifr); }, 2000); }
另外,可以通過 document.hidden
或 document.[webkit|moz|ms]Hidden
來判斷頁面是否被置入后台(即應用被喚起),或visibilitychange
事件,但對於Android 4.4版本一下則不支持。
iOS9
在 iOS 9 上,iframe 方案變得不可用。
按不能使用之前Android的代碼,因為在打開自定義 URL scheme 時,會彈出對話框,詢問是否用 xx 應用來打開。往往用戶還沒來得及點擊打開,定時器又觸發了,導致跳到 App Store。
可以在嘗試打開URL scheme 后,再加一個頁面跳轉,這樣對話框會被覆蓋,再刷新頁面,就能無需確認喚起APP:
$('a').click(function() { location.href = '自定義 URL scheme'; location.href = '下載頁'; location.reload(); }
這里,下載頁延時 2 秒跳轉到 App Store。
APP已安裝這是沒問題的,但如果APP未安裝,跳 App Store 的請求會失敗。
這時可以使用兩個定時器:
$('a').click(function() { location.href = '自定義 URL scheme'; setTimeout(function() { location.href = '下載頁'; }, 250); setTimeout(function() { location.reload(); }, 1000); }
不過在iOS9中其實是支持universal link的,就是一個http域名形式,在微信中都可以喚起APP。如果未安裝的話,可以直接引導用戶去APP store下載。
可以參考這篇文章
http://www.magicwindow.cn/doc/#universal-link-info
沒有完美的解決方案
主要是在安卓上,總歸會有各種兼容問題,知乎的解決辦法是,提供兩個按鈕,一個下載,一個打開APP,讓用戶自己選。
微信中打開
因為微信將喚起本地APP的接口給禁了,所以微信中是不能直接喚起APP的,一般做法是提示用戶在瀏覽器中打開,之后的流程還是我們上面講的內容。
但是,在iOS9中,這個限制是可以突破的,也就是說可以直接喚起APP。方法就是使用我們上文提到的universal link。
在Android和iOS8及其以下系統中,我們可以利用騰訊的親兒子:應用寶。簡單講,就是把你的喚起地址配置成你APP的應用寶地址,微信中跳轉到這個地址后,如果用戶已經安裝了APP,則可直接喚起,如果沒有安裝,則可直接點擊下載,如下圖示:
但這里有坑需要注意。
對於使用universal link來說,如下圖所示用戶在微信中打開APP之后,可能不小心點擊右上角的鏈接(比方說點幾分享,卻不小心點擊了"mlinks.cc"),導致跳到外部瀏覽器中,如下圖所示:
這時候再在微信中就打不開APP了,因為universal link已被關閉,這是iOS9的機制,沒法改變,這時候用戶再在微信中打開,就得需要一個中間頁來引導用戶在外部瀏覽器中打開APP,如下圖所示:
另外,在微信中喚醒APP默認只能到達首頁,即不能到達指定頁面或內容,如果想要做,則需要額外的處理。
拿來主義
從以上內容可以總結出:要做一個兼容性很好的方案,就需要考慮各種情況,在不同的情況適配不同的方案,比方說用戶是在手機瀏覽器打開還是微信中打開,或者是在pc中打開,universal link是否被關閉等,這就使代碼實現變得復雜,且容易出錯,且還有安卓平台機型眾多、瀏覽器眾多等導致的兼容問題。
如果覺得實現難度或者成本太高,你可以考慮使用魔窗的mLink。只要你加了魔窗的sdk,就可以通過類似“https://s.mlinks.cc/AA01”的鏈接,在任何環境下打開你的APP(如果在pc機上打開,瀏覽器中將會出現APP下載地址的二維碼),上面提到的問題都不復存在,並且魔窗已經兼容超過600台以上安卓機型的第三方主流瀏覽器。而且關鍵的是,不管是在手機瀏覽器中,還是在微信中打開,你可以指定喚起APP后直達APP中的某個頁面或內容(某個促銷商品等),就算用戶沒安裝APP,點擊下載安裝之后,再打開,還是跳轉到指定的頁面,這就是場景還原,或者叫做Deffered Deep Linking。
歡迎訪問魔窗官網:http://www.magicwindow.cn/