在移動端瀏覽器H5頁面中,點擊按鈕打開本地應用主要通過 scheme 協議。本文主要介紹如何在瀏覽器H5頁面中通過 scheme 協議打開本地應用。
scheme協議定義
scheme 是一種頁面之間跳轉的協議,不僅可以用於app之間進行跳轉,還可以用於 H5 頁面跳轉到app頁面。
無論Android還是IOS,都可以通過在H5頁面中打開 scheme 協議的地址,從而打開本地app。
scheme 協議定義和 http 協議類似,都是標准的 URI 結構。
[scheme:][//host:port][path][?query][#fragment]
- scheme : 協議名稱 - 必須
- host : 協議地址 - 必須
- port : 協議的端口,可以不填
- path : 協議路徑,可用 / 連接多個
- query : 攜帶的參數可用 & 連接多個
- fragment : 錨點
下面看一個例子:
wexin://tencent.com:8080/dl/news/open?data=902323¶ms=test
- weixin : 協議名稱
- tencent.com : 域名
- 8080 : 端口
- /dl/news/open : 頁面的路徑
- data, params : 傳遞的參數
URI中的參數如果包含特殊字符,需要預先進行url編碼,否則的話URI可能不能打開。
在 Android 中聲明實現 scheme
要使得在瀏覽器或者別的應用中通過打開 scheme 協議來喚起應用,需要對該應用進行相關的配置。
首先需要在Android工程的 Manifest文件,給想要接收跳轉的Activity添加 intent-filter 節點的配置攔截器規則
<activity
<!--定義響應該scheme協議的 activity 的名稱 -->
android:name=".DeepLinkActivity"
<!--需要添加下面的intent-filter配置-->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<!--scheme 允許在瀏覽器中打開-->
<category android:name="android.intent.category.BROWSABLE"/>
<!--scheme 相關信息配置-->
<data android:scheme="uuopen"
android:host="uusama.com"/>
</intent-filter>
</activity>
上面的 data 節點中可以包含下面的信息來對相應的scheme進行過濾,一般需要配置 scheme 和 host。
<data
android:scheme=""
android:host=""
android:port=""
android:path=""
android:mimeType=""
android:pathPattern=""
android:pathPrefix=""
android:ssp=""
android:sspPattern=""
android:sspPrefix=""/>
然后在相應的 activity 可以獲取 uri 中參數。
public class DeepLinkActivity extends AppCompatActivity {
private static final String TAG = "DeepLinkActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
Log.e(TAG, "scheme:" + intent.getScheme());
Uri uri = intent.getData();
Log.e(TAG, "scheme: " + uri.getScheme()); // 獲取 scheme 名稱
Log.e(TAG, "host: " + uri.getHost()); // 獲取 scheme 的host
Log.e(TAG, "path: " + uri.getPath()); // 獲取 scheme 的路徑
Log.e(TAG, "queryString: "+ uri.getQuery()); // 獲取 scheme 的參數,?后面的部分
Log.e(TAG, "queryParameter: " + uri.getQueryParameter("param")); // 獲取 scheme 中的 param 參數
}
}
其中的 intent 實例有下面的方法可以獲取相應的 scheme 信息:
- getScheme() :獲取Uri中的scheme名稱:[scheme:]
- getSchemeSpecificPart() :獲取Uri中的scheme-specific-part:部分:[//host:port][path]
- getFragment() :獲取Uri中的Fragment部分:[#fragment]
- getAuthority() :獲取Uri中Authority部分:[//host:port]
- getPath() :獲取Uri中path部分:[path]
- getQuery() :獲取Uri中的query部分:[?query]
- getHost() :獲取Authority中的Host字符串
- getPost() :獲取Authority中的Port字符串
- List< String> getPathSegments() :依次提取出Path的各個部分的字符串,以字符串數組的形式輸出
- getQueryParameter(String key) :獲取query部分中 key 對應的參數值
在瀏覽器中打開 scheme
在瀏覽器中打開 scheme 就像打開一個不同的http地址一樣。可以在一個 a 標簽中打開。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Scheme</title>
</head>
<body>
<a href="wexin://" id="open">打開應用</a>
</body>
</html>
點擊上面的H5頁面中的鏈接將會嘗試喚醒微信,在一些瀏覽器中,可能會彈出一個提示框,詢問用戶是否允許打開應用。
如果打開的 scheme 在本地沒有對應的 app,則點擊連接不會反應。
當然還可以使用 JavaScript 代碼打開,只需要添加相應的事件觸發和處理即可。
在JavaScript代碼中打開連接有以下幾種方式:
- 新建一個隱藏的 iframe ,地址指向需要打開的url
- 使用 window.location 或者 window.location.href 刷新當前頁面
- 新建一個隱藏的 a 標簽,地址指向打開的url,並觸發打開鏈接事件
- 動態創建一個script腳本,在這個腳本中新建一個a標簽並打開
// 打開url的方式
var urlOpen = {
'iframe' : function(url) {
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
},
'location' : function(url) {
window.location = url;
},
'href' : function(url) {
var a = document.createElement('a');
a.style.display = 'none';
a.href = url;
document.body.appendChild(a);
a.click();
},
'script' : function(url) {
var script = document.createElement('script');
script.setAttribute('type', 'test/javascript');
script.innerHTML = '(function(){' +
'var a = document.createElement("a");' +
'a.style.display = "none";' +
'a.href = "' + url.replace(/"/g, '\\"') + '";' +
'document.body.appendChild(a);' +
'a.click();' +
'})()';
document.body.appendChild(script);
},
'open' : function(url) {
window.open(url);
}
};
瀏覽器判斷是否安裝應用
很多時候用戶在瀏覽器中打開 scheme 鏈接的時候,用戶不一定安裝了應用,這個時候打開會失效,我們希望打開這個動作應該下載應用。這個時候需要判斷用戶是否安裝應用。
其實判斷用戶是否安裝某個應用的方法,就是直接打開這個應用的 scheme,查看是否打開成功。但是這就是問題之所在。
我們無法在瀏覽器中准確地知道打開 scheme 是否成功,瀏覽器或者系統沒有給我么這樣的回調。我們只能用迂回的方法去判斷。
比如在JavaScript中判斷頁面是否進入后台來判斷打開成功。有下面這些事件和屬性可以利用:
- pagehide : 頁面隱藏時觸發
- visibilitychange : 頁面隱藏沒有在當前顯示時觸發(切換tab也會觸發該事件)
- document.hidden : 當頁面隱藏時,該值為true,顯示時為false
上面這些事件或者屬性並不是所有瀏覽器都支持。下面是一個給出為 id 為 open 的按鈕添加打開scheme或者下載事件的例子。
var downloader,
scheme = 'weixin://', // 需要打開的 scheme 地址
download='index'; // 如果打開scheme失效的app下載地址
// 給 id 為 open 的按鈕添加點擊事件處理函數
document.getElementById('open').onclick = function () {
window.location.href = scheme; // 嘗試打開 scheme
// 設置3秒的定時下載任務,3秒之后下載app
downloader = setTimeout(function(){
window.location.href = download;
}, 3000);
};
document.addEventListener('visibilitychange webkitvisibilitychange', function () {
// 如果頁面隱藏,推測打開scheme成功,清除下載任務
if (document.hidden || document.webkitHidden) {
clearTimeout(downloader);
}
});
window.addEventListener('pagehide', function() {
clearTimeout(downloader);
});
}
對於通過判斷打開 scheme 的耗時來確實是否打開應用的做法是很容易失效的,因為無法判斷打開成功以后,頁面的JS是否還在執行,而且打開應用的耗時也是不可控的。
總之,沒有完美的解決方案在H5頁面中判斷本地是否安裝了某個應用,不過使用監聽當前頁面是否隱藏的方法能夠很大程度的作為判斷依據。
也有的應用不管用戶是否安裝應用,用戶點擊鏈接的時候,同時打開 scheme 和拉起下載頁面,這種方式犧牲了很大的用戶體驗。
局限性
這種通過 scheme 打開本地應用的方式並不是所有瀏覽器都支持,尤其是在微信瀏覽器中是不支持使用 scheme 打開應用的,除非微信官方添加了白名單。QQ瀏覽器中倒是支持。
而且一些瀏覽器會詢問用戶是否打開,而另外一些則直接打開應用。
一般的做法是,判斷當前瀏覽器是否為微信,如果是微信的話,則彈出一個遮罩層,提示用戶使用其他瀏覽器打開。
還有就是在微信瀏覽器中使用應用寶的微下載,將當前頁面重定向到應用寶的下載頁面,不過這種方式的轉化率很低。
有一個好消息是,在IOS9.0以上的系統中,可以使用 universal links 打開本地應用,不過Android不支持。
另外一個備選方案是,在微信瀏覽器中,使用iframe的方式打開一個包體地址(.apk結尾的url)進行下載時,會拉起一個選擇框,讓你選擇打開的應用。不過這種方式對於某些域名無效,對於一些特殊的下載文件無效,如不能下載.rar的文件。而且對於已經安裝了應用的用戶來說,用戶體驗也不好。
使用這種技術的同時,考慮到其不確定性,應該做好備選方案。充分考慮到該頁面的用戶群體是否主要為新用戶,以及訪問的瀏覽器分布,從而制定相應的對用戶來說比較友好的引導和備選方案。