喚起 App 在轉轉的實踐
轉轉 App 是我們公司最核心的產品,引導用戶到轉轉 App 中來,對用戶增長和留存十分重要。常見的做法是在各個流量入口,投放 Web 版的引流頁面,然後通過該頁面喚起 App(下文簡稱 「喚端」 )。本文重點介紹轉轉是如何在各種場景下,完成整個喚端流程的。
喚端功能架構
我們先通過一張 Gif 圖,直觀的看下喚端是什麼
不過細心的同學一定會問,那如果我係統沒裝轉轉 App,點擊按鈕會是什麼效果呢?
這是一個很常見的場景,對於沒有安裝相關 App 的用戶,往往會需要跳轉到下載頁面,讓用戶去下載。
也就是說喚端一般包含了 「喚起 App」 、 「下載 App」 以及 「喚起 App 失敗後自動下載」 這三個功能。
再進一步想一下,喚端功能還需要能喚起指定頁面,比如一個用戶在下單頁,喚起 App 後,我們希望 App 默認打開的還是那個下單頁,不然讓用戶再走一遍流程到下單頁,那對訂單轉化率會有不小的影響。
總結一下,我們用一張流程圖來說明下喚端整體功能架構
喚端技術架構
聊完了喚端是什麼,接下來進入主菜,聊聊喚端具體的技術架構
喚端所使用的技術,通常可以統稱爲 Deep Link(深度鏈接)技術。不同平臺對這項技術有着不同的實現,主流的有這幾種
-
URL Scheme(全平臺通用)
-
Universal Link(通用鏈接,iOS 系統專屬)
-
App Links 以及衍生的 Chrome Intents(安卓系統專屬)
當然,以上只是國際通用標準,我們還需要考慮到 “國內特色”,包括但不限於 「微信爸爸」 、微博、UC 瀏覽器等這些環境的喚端,對於這些 App,主流的喚端方式有這麼幾種
-
Universal Link(部分 App 可用,快被國內主流 App 禁乾淨了)
-
微信 API
launchApplication
和getInstallState
(需要申請白名單,難度很高) -
微信開放標籤
<wx-open-launch-app>
(微信推薦方式,需要審覈,流程較繁瑣) -
跳轉到應用寶,然後通過應用寶喚端 (適用於微信安卓環境)
-
彈出個蒙層,友好的提示用戶,點擊右上角按鈕,然後選擇在瀏覽器中打開 (easy~)
看過去有點天花亂墜,老樣子,我們通過一張功能架構圖,完整的看看喚端的技術架構
常見喚端方法詳解
這部分,我們主要介紹下 URL Scheme
、Universal Link
、微信喚端
和 自家公司App
四種技術場景下的喚端實現
URL Scheme
URL Scheme 其實就是一個 URL 前面的協議部分,比如這個地址 https://m.zhuanzhuan.com
,其中 https
就是一個 Scheme,代表這是一個https
的地址。
我們再看一個地址 zhuanzhuan://jump/core/myBuyList/jump?tab=0
,當我們訪問這個地址的時候,如果系統中有註冊 zhuanzhuan:// => 轉轉App
,那麼系統便會使用轉轉 App 打開這個鏈接了,接着 App 會解析 URL 的 path 和 search 部分,執行對應的操作。
那麼如何向系統註冊 zhuanzhuan
這個 Scheme 呢,安卓可以在 manifest
裏通過 intent-filter
配置,iOS 則可以在 info.plist
文件中添加 URL types
來註冊一個 Scheme。
Scheme 的調用方法
Scheme 的調用也十分簡單,我們可以通過 location.href
或 iframe
或 a標籤
來調用。
這三種方式並無本質區別,都是讓系統訪問一個 URL,只不過在某些環境下,當方法失效時,需要採用另一種形式。
經過長時間的摸索,我們總結了如下最佳實踐
-
在 iOS 的 QQ 環境,使用
a標籤
方式調用 URL Scheme 更加穩定 -
非上述情況,使用
location.href
調用 URL Scheme 即可 -
如果希望 URL Scheme 出錯時,喚端不會跳轉到錯誤頁面,那麼可以使用
iframe
的形式,不過需要注意個別機型的兼容性
三種調用方式的具體代碼如下
// SCHEMA_PATH 爲 URL Scheme 地址
// location.href 形式
window.top.location.href = SCHEMA_PATH;
// a標籤形式
const a = document.createElement("a");
a.setAttribute("href", SCHEMA_PATH);
a.click();
// iframe形式
iframe = document.createElement('iframe');
iframe.frameBorder = '0';
iframe.style.cssText = 'display:none;border:0;width:0;height:0;';
document.body.append(iframe);
iframe.src = SCHEMA_PATH;
喚端失敗自動下載
使用 URL Scheme 會遇到一個棘手的問題,那就是我們無法得知是否成功喚起 App。這個也可以理解,因爲這個方式本質上就是訪問一個 URL,並沒有特別的地方。
但是喚端失敗(通常是用戶沒有裝這個 App),我們需要引導用戶去下載,這就要求我們需要通過一些手段,去檢測喚端是否成功。
常見的做法是通過監聽頁面是否在 n 秒內 隱藏來判斷是否成功喚起 App,因爲如果喚起,那當前頁面必然已經退到後臺去了。
所以我們處理的流程是這樣
-
當點擊喚端後,定時器延時 n 秒 (我們設定的是 2.5 秒) 後執行下載操作
-
監聽
visibilitychange
事件,如果頁面隱藏,則表示喚端成功,清除定時器 -
如果
visibilitychange
事件沒有被觸發,那麼就代表喚端失敗,n 秒後就會執行下載的操作
具體代碼如下
// n秒後執行下載
const timer = setTimeout(() => {
this.__download(options);
}, options.delay);
// 頁面隱藏,那麼代表已經調起了app,就清除下載的定時器
const visibilitychange = function() {
const tag = document.hidden || document.webkitHidden;
tag && clearTimeout(timer);
};
document.addEventListener("visibilitychange", visibilitychange, false);
document.addEventListener("webkitvisibilitychange", visibilitychange, false);
window.addEventListener(
"pagehide",
function() {
clearTimeout(timer);
},
false
);
不過這個方法有個致命問題,就是 n
應該取多少,不同的手機,喚端的時間並不一樣。
-
時間設置太長,用戶下載等待太久,會導致用戶還沒看到下載就退出了。
-
時間設置太短,可能會導致還沒有喚起 App,就又同時執行了下載的邏輯。
這裏我們經過測試,覺得 n 設置爲 2500ms-3000ms,是一個比較合適的值
URL Scheme 在 iOS 上的問題
上面說到了喚端失敗自動下載的場景。在實際項目中,我們會發現在 iOS 上使用 URL Scheme 還有一個問題,就是即執行了喚起,也執行了下載。經過測試我們得到了如下流程
-
用戶點擊喚端按鈕(記錄 Time1)
-
Safari 彈出是否在 “轉轉” 打開的 confirm 框
- 此時雖然彈出了 alert 框,但是並未阻斷代碼運行,所有代碼仍在運行中,只是界面沒變
-
用戶點擊打開轉轉(記錄 Time2),跳轉到轉轉
-
用戶返回 Safari,如果 Time2 - Time1 的時間大於設置的時間
n
,那麼之前的邏輯代碼便會繼續運行。也就會出現喚端成功後,又繼續走下載邏輯
對於這個問題,大家看這個動圖更直觀些,第一次是點擊喚端後,停留了一會才點擊打開。第二次是立刻點擊打開
Universal Link
上面提到了 URL Scheme 在 iOS 的一些問題。其實在 iOS 系統中,有一種更好的方式,那就是接下來要介紹的 Universal Link。
Universal Link 是在iOS 9
中新增的功能,使用它可以直接通過https
協議的鏈接來打開 APP。
同時因爲是使用https
協議,所以如果沒有喚端成功,那麼就會直接打開這個網頁,不再需要判斷是否喚起成功了。並且使用 Universal Link,不會再彈出是否打開的彈出,對用戶來說,喚端的效率更高了。
我們先看一個 Universal Link 喚端的例子,比如這個地址 https://mjump.zhuanzhuan.com/zhuanzhuan/index.html
,直接訪問它是一個下載引導頁,但是在項目中,通過 location.href
跳轉,卻可以直接喚起轉轉。
PS:點擊按鈕時,執行的代碼是 location.href = https://mjump.zhuanzhuan.com/zhuanzhuan/index.html
Universal Link 的配置過程,網上的文章比較零散細碎,這裏我梳理下它的原理與關鍵點,把流程串起來,方便理解,避免大家採坑
- app 在發佈的時候,在
Associated Domain
配置好一個域名
- 該域名與今後要喚端頁面的域名,不能一樣。比如 轉轉 web 的域名是
m.zhuanzhuan.com
,那麼這個域名就不能用它了,可以用mjump.zhuanzhuan.com
- iOS 系統在 「app 安裝」 的時候,會請求配置在
Associated domain
中域名(在這裏就是https://mjump.zhuanzhuan.com
)的根路徑或者.well-known
路徑的配置文件apple-app-site-association
,並把它註冊到系統中
- 這個文件的內容大體如下,其中 appID 就是 App 在系統中的 id,
paths
我們後面會提到
{
"applinks": {
"apps": [ ],
"details": [
{
"appID": "ECVV5G85CD.com.wuba.zhuanzhuan",
"paths": [
"zhuanzhuan/*",
"/qq_conn/1104903352/*"
]
},
{
"appID": "ECVV5G85CD.com.zhuanzhuan.yige",
"paths": [
"yige/*"
]
},
{
"appID": "75BF75R4NH.com.wuba.*",
"paths": [
"*"
]
}
]
}
}
- 當頁面進行**「跨域訪問」**時,比如從
https://m.zhuanzhuan.com
跳轉到https://mjump.zhuanzhuan.com/zhuanzhuan/index.html
,因爲mjump.zhuanzhuan.com
在第 2 步的時候,已經被系統註冊過了,所以系統會把頁面的 path,也就是zhuanzhuan/index.html
部分,和上面的配置文件中的 paths 進行匹配,如果能匹配上,那麼就會喚起配置文件中對應的 App。如果匹配不上,或者用戶沒有安裝該 App,那麼就正常進行地址訪問
-
因爲喚端失敗會訪問
https://mjump.zhuanzhuan.com/zhuanzhuan/index.html
地址,所以這個地址不能是 404 -
直接在瀏覽器訪問這個地址,並不會喚起 App,因爲不是**「跨域訪問」**
-
喚起 App 後打開指定頁面,可以通過在 URL 上增加參數,然後 Native 解析參數的形式來實現
微信喚端
微信作爲一個超級 App,如果能借助微信的流量進行喚端,想想也很開心 😁。不過微信對喚端做了十足的限制,用起來可沒有那麼容易
微信通用喚端能力
微信7.0.5
版本放開了 Universal Link,所以在 iOS 系統下,使用 Universal Link 就可以在微信完成喚端。
在實際使用中,我發現了一個奇怪的問題,就是有些 App 可以在微信直接喚起到另一個 App,中間沒有任何提示。而轉轉在喚端的時候,會有一個 “可能離開微信,打開第三方應用” 的提示 😹
在安卓端,我們可以使用應用寶進行微信喚端。先跳轉應用寶,然後通過應用寶再喚起相應的 app,同時應用寶也開放了應用寶 App Link
,可以做到喚起指定頁面
微信 API - launchApplication
早期微信提供了launchApplication
和getInstallState
方法來進行喚端。其中launchApplication
可以直接喚起 App,getInstallState
可以檢查某個 App 是否安裝(僅限安卓)。
開通了白名單的應用,可以通過這兩個 API 進行喚端。沒開通的同學,現在應該很難開通了,可以使用微信推薦的開放標籤來進行喚端
這裏簡單梳理下launchApplication
的使用流程
- 首先拿到簽名進行 config 配置
wx.config({
debug: false,
appId: conf.appId,
timestamp: conf.timestamp,
nonceStr: conf.noncestr,
signature: conf.signature,
beta: true,
jsApiList: ['launchApplication', 'getInstallState'], // 使用這兩個API
openTagList: ['wx-open-launch-app'],
})
- 使用如下兩個 API 進行喚端操作
/**
* @description 微信拉起第三方App方法
* @param {Object} options -必填項,以json形式傳參
* @param {String} options.appID - 必填項,供iOS使用
* @param {String} options.parameter - 必填項,schema://parameter 通過系統接口拉起第三方app。中文或特殊字符需要encode
* @param {String} options.extInfo - 必填項,該參數僅Android使用,對應Android微信opensdk中的extInfo,格式自定義,由第三方APP自行解析處理ShowMessageFromWX.Req 的微信回調
* */
launchApplication(options) {
return WeixinJSBridge.invoke('launchApplication', options, res => {
console.log(res)
})
}
/**
* @description 微信判斷第三方App是否安卓 (僅僅Android支持, IOS無效、無法判斷)
* @param {Object} options -必填項,以json形式傳參
* @param {String} options.packageName - 必填項,安卓的包名
* @param {String} options.packageUrl - 必填項,schema 協議
* */
getInstallState(options) {
return WeixinJSBridge.invoke('getInstallState', options, res => {
console.log(res)
})
}
經過上面的配置後,我們可以看一下微信端喚端的演示動圖
微信開放標籤 - wx-open-launch-app
微信開放標籤是微信官方提供的喚端組件,具體使用方式可以參考官方文檔 [1]。
這裏有個注意點,就是該功能需要一個公衆平臺的服務號和開放平臺的賬號以及應用進行綁定。因爲我們公司的 App 綁定的公衆平臺與開放平臺主體並不一樣,所以這個功能沒使用成功,有需求的同學可以試試
開放平臺與公衆平臺的區別,可以參考筆者過去的文章 (快速瞭解微信開放生態體系 - https://zhuanlan.zhihu.com/p/189968202)。
自家 App 喚起其他 App
最後要說下自家的 App 去喚起類似支付寶、微信等應用的情況。其實這個吧,對 FE 來講也並不複雜,讓 Native 人員提供一個方法給咱調用就行 😁。
App 可以調用系統的 API,比如 openURL
等,可以很方便的喚起其它 App。這塊對我們來說,需要注意的就是動態引入 App 的 sdk,保證喚起的時候 sdk 初始化完成。動態引入的具體邏輯,在源碼中可以查閱到~
源代碼
喚端的細節判斷比較多,一篇文章很難說清楚,我們把轉轉使用的一個穩定版本的喚端庫開源,方便大家交流學習,代碼寫的一般,主要供大家參考,同時也歡迎大家對代碼進行吐槽,你們的吐槽是我們重構的源動力 😁
源碼地址:https://github.com/zhuanzhuanfe/call-app
Reference
[1] 官方文檔: https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_H5_Launch_APP.html
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/MNrMDku3C9rNzq8KREuoXQ