iOS/Android 微信及浏览器中唤起本地APP
title: iOS/Android 微信及瀏覽器中喚起本地APP
date: 2017-05-10 10:19:20
tags:
需求概述
分享應用活動鏈接已經成為手機應用一個非常重要的推廣傳播形式。為了提高轉化率,就需要讓用戶不管是在微信或者是瀏覽器中,都能在點擊鏈接后,?喚起本地的 app 后?,?跳轉到指定頁面?。
雖然這個功能從用戶體驗方面來說是自然而然的,但是由于 iOS/Android 平臺差異性,在實現過程中還是有些問題。
未安裝 app 時,如何做好引導頁,引導用戶下載后打開 app 后,是否可以打開之前喚醒前指定的頁面或內容
如何繞過微信的 ?scheme? 屏蔽,在微信中喚醒 app 中,并打開指定頁面
iOS 專用的 ?universal link?,Android 專用的 ?App Links? 的集成要求,有哪些局限性。
現實情況
在實施過程中,還是有兩個地方是沒辦法忽略的:
scheme 被微信屏蔽了
除非一些和微信有合作的 app 可以進入到白名單,其他的應用在微信內都沒辦法通過自定義 scheme 協議直接喚起 app,前端頁面需要對喚起場景進行判斷。
瀏覽器無法明確地判斷本地是否已經安裝 app
目前的取巧方案就是通過 ?setTimeOut? 設置超時時間,在超時時間內喚起 app,然后獲得成功失敗回調,如果獲得的是失敗的回調,則說明本地沒有安裝 app,需要跳轉到商店下載頁面。
實現方案
踩坑方案
鑒于在打開 url scheme 的方法中,iOS9 和 iOS8/iOS7 區別很大,Android 不同廠商的是適配也不同,這里介紹的踩坑方案都是前人實踐總結出來的。
兩種打開方式:
一、 直接跳轉:點擊鏈接或者修改 ?window.location?。
點擊鏈接:
修改 window.location:
window.location.href = schemeUrl;這種情況,如果APP喚醒失敗,或者APP未安裝的話,很多時候都會跳到錯誤頁,這很影響用戶體驗,而我們的要求可能是跳轉到其他頁面或者下載APP。
二、?iframe? 跳轉:在 body 上添加 iframe,設置 src 屬性為跳轉的 URL scheme
<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 標簽時,打開 自定義 scheme。若成功,就喚起 app,若失敗,就到 href 屬性,去到下載地址。
相應的,在 Android 客戶端這邊,需要在 manifest 文件里面配置 intent-filter,如下:
<intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="自定義 URL scheme" /> </intent-filter>注意這里的 ?intent-filter? 的這幾個配置不能再和?action.MAIN?放在一起。如果是在同一個 activity 中配置,那么可以配置兩個?intent-filter?,比如
<!-- 第一個filter --> <intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /> </intent-filter><!-- 第二個filter --> <intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="mls" /> </intent-filter>理論上,在 Android 上這兩種方式應該都是可以順利實現的。但是 Android 的 chrome 內核從 chrome 25 以后就棄用了 iframe,不再支持通過 js 觸發(非人為點擊)或者通過設置 ?iframe src? 地址來觸發 scheme 跳轉。所以,后一種方法在適配上存在比較多的問題。故一般選擇前面一種做法,即 href 的點擊或者 window.location 跳轉。
使用第一種方案,setTimeOut 派上用場。
$('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 毫秒,很可能是沒有安裝應用,就跳到下載地址。
但是這么做也是有問題的,因為 Android 系統是多任務系統,setTimeOut基本就沒那么精準,不能起到理想效果。換一種方式,?setInterval?,如果設置比較小的運行間隔(<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 中打開 appvar 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); }iOS9 上 iframe 也不可用
在 iOS 9 上,iframe 方案變得不可用。在打開自定義 URL scheme 時,會彈出對話框,詢問是否用 xx 應用來打開。往往用戶還沒來得及點擊打開,定時器又觸發了,導致跳到 App Store。
可以在嘗試打開URL scheme 后,再加一個頁面跳轉,這樣對話框會被覆蓋,再刷新頁面,就能無需確認喚起 app:
APP已安裝這是沒問題的,但如果APP未安裝,跳 App Store 的請求會失敗。 這時可以使用兩個定時器:
$('a').click(function() {location.href = '自定義 URL scheme';setTimeout(function() {location.href = '下載頁';}, 250);setTimeout(function() {location.reload();}, 1000); }universal link 和 App Links
這里的鏈接指的是深度鏈接?(deep learning)?,這是一種能夠方便地通過傳統的 http 鏈接來啟動 app 或網站。通過唯一的網址,就可以鏈接一個特定的視圖到你的 app 里面不需要特別的 scheme。
iOS 的 universal link
使用要求:
使用步驟:
添加域名到 ?Capabilities?
首先, 你必須在 Xcode 的 capabilities 里 添加你的 APP 域名, 必須用 applinks: 前置它:還添加一些你可能擁有的子域和擴展(www.domain.com, news.domain.com 等等)。這將使你的 APP 從你的域名請求一個特殊的 JSON 文件 apple-app-site-association。當你第一次啟動 APP,它會從 https://domain.com/apple-app-site-association 下載這個文件。
構建 ?apple-app-site-association? 文件
該文件必須存在且為了安全原因可使用 SSL 通過 GET 請求訪問到。你可以打開一個文本編輯器然后寫一個這樣的簡單 JSON 格式:
根據 paths 鍵設定一個允許的路徑列表(你希望APP 作出反應的路徑), 設置 * 號則只是打開 app 而已。
TEBEJCSf9DF 這一串是具有團隊標示的 bundle id。
文件構建成功后,上傳這個文件到你的域名根目錄。
在 app 里處理通用鏈接
為了在 app 里支持通用鏈接, 需要在 AppDelegate 里實現 ?[application(_:continueUserActivity:restorationHandler:)]? 。 盡管這種方法可以用于許多不同的目的(比如 [Handoff]和 [搜索 API]), 我們將只關注如何處理接收到的通用鏈接。
為了確保 app 可以翻譯 URL 成實際的內容, 需要做下面幾步:
-
使用 [NSURLComponents]簡單解析 webpageURL 到 host(如domain。com), 路徑組成同理(如 ["/"]、“path”、“to"以及"thezoo”)。
-
確保能識別 host。
-
嘗試將 pathComponents 匹配到 APP 的已知內容里。
-
驗證該內容實際上可以被呈現并呈現內容給用戶。
Android 的 App Links
Android 手機上,打開鏈接通常會跳出選擇框選擇用什么瀏覽器打開,而使用 app links,當點擊了鏈接,安卓系統會檢查是否有一個 app 可以處理 url(比如 twitter.com),然后跟核對哪個app(s)可以處理該域名的鏈接,直接在應用內處理,這樣我們就能避免彈框影響用戶。
和 iOS 一樣,Android 的深度鏈接也需要相關的域名來配合,有以下的條件:
想要讓你的 app 處理鏈接,需要在 manifest 文件中使用 intent filter 聲明 app 需要處理的 uri 模式。下面的例子,聲明了一個 ?intent filter? 能夠處理 http://www.android.com 和 https://www.android.com:
<activity …><intent-filter android:autoVerify="true"><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="http" /><data android:scheme="https" /><data android:host="www.android.com" /></intent-filter> </activity>配置 ?autoVerify?來觸發系統自動校驗。
如何驗證成功?這就需要網站所有者必須聲明和app的聯系。網站所有者通過持有一個名為 ?assetlinks.json? 的 Digital Asset Links JSON 文件來聲明與一個 app 的聯系,它在 domain 的well-known 位置:https://domain[:optional_port]/.well-known/assetlinks.json。
注意:系統通過加密的 HTTPS 協議來驗證 json 文件,所以不管 intent filter 中是否聲明了 https,請確保 json 文件能夠通過 HTTPS 連接來獲取。
Digital Asset Links JSON 文件聲明了與此網站關聯的 app,下面的示例 assetlinks.json 文件允許包名為 com.example 的app打開鏈接:
sha256 可以通過 keytool 來獲取。
預想可操作方案
鏈接頁面里面有兩個按鈕:直接打開和下載。直接打開的話,判斷本地是否存在 app 后直接啟動 app,下載的話去到下載頁面。前端需要設計兩個頁面(分享的頁面,下載的頁面)。
使用<a>標簽的方式,來啟動客戶端。
區分渠道,判斷瀏覽器內核區分是 Android 還是 iOS,在 Android 6.0 以上,可使用 app links,在 iOS9.0 以上,可使用 universal link.但是這兩個都需要提供應用網站域名來綁定。其他系統版本通過自定義 scheme 來截取。
補充下 Android 的調用流程:
由于好多初始化工作都在 SplashActivity 里面做了,所以開啟的流程也是從上到下一步一步開頁面,數據也需要傳遞過去。遇到需要登錄的時候,先跳登錄頁,再跳詳情頁,把登錄頁從棧中清掉,返回時,就能從詳情頁 DetailActivity 回到 MainActivity 了。
關注全棧公眾號,遇見更好的自己。
總結
以上是生活随笔為你收集整理的iOS/Android 微信及浏览器中唤起本地APP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 狂神说笔记——Java SE基础01
- 下一篇: android 沉浸式状态栏导致布局被遮