在移動互聯網,連接是比較重要的傳播媒質,但不少時候咱們又但願用戶可以回到APP中,這就要求APP能夠經過瀏覽器或在微信中被方便地喚起。javascript
這是一個既直觀又很好的用戶體驗,但在實現過程當中會遇到各類問題:html
下面是我一些我的的經驗分享。java
這塊內容其實比較簡單,在網上都有不少資料可供查閱,就再也不贅述。android
首先須要說明,無論iOS仍是Android,瀏覽器都不可能預知本地是否安裝了某個APP的。或者更嚴謹地說,咱們不能經過瀏覽器來預知本地是否安裝。由於就算瀏覽器能夠讀取本地應用的安裝列表,可是目前也沒任何一家瀏覽器提供查詢的API,因此這條路是走不通的。ios
本質上瀏覽器是經過URL scheme打開APP,一個APP能夠設置一個或多個打開本身的URL scheme。好比,Twitter就註冊本身能被「twitter://」打開。web
其實,若是是作APP間相互跳轉是比較簡單的。iOS就可使用 UIApplication 的 canOpenUrl 方法來檢測URL scheme 是否能打開對應的APP。好比,若是「twitter://」檢測能被打開,也就說明本地安裝了 Twitter 。再用 UIApplication 的 openURL 方法,就能打開Twitter了。Android 中的作法相似。瀏覽器
由於iOS9和以前的iOS系統有區別,因此這裏咱們也要區別對待。微信
iOS中默認經過Safari打開URL scheme,方法通常以下兩種:app
第一種狀況:ui
<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 屬性,即下載頁。
但這個方案在不少安卓機型上有問題,爲保證可用,改用第一種方案:
$('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版本一下則不支持。
在 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/