一、引言
有這么一個需求,點擊h5頁面上的一個按鈕,需要判斷本機(jī)有沒有安裝某app,若已經(jīng)安裝過,則直接調(diào)起此app,若沒有安裝,則跳轉(zhuǎn)該app的下載頁。要求安卓手機(jī)跳轉(zhuǎn)此app的下載包地址。ios跳轉(zhuǎn)此app的APP Store。
二、喚起方法
URL Schemes
[scheme:][//authority][path][?query][#fragment] 比如:app://weixin?test=1
行為(應(yīng)用的某個功能)
|
scheme://[path][?query]
| |
應(yīng)用標(biāo)識 功能需要的參數(shù)
URL Schemes:好比給手機(jī)APP分配一個特殊格式的 URL,用來訪問這個APP或者這個APP中的某個功能(來實現(xiàn)通信)。APP得有一個標(biāo)識,好讓我們可以定位到它,它就是 URL 的 Scheme 部分。
*注意:應(yīng)用是否支持
URL Schemes要看App開發(fā)者有沒有寫那部分的代碼了
Intent
安卓的原生谷歌瀏覽器從
chrome25版本之后就不能通過URL Schemes喚醒安卓應(yīng)用。要使用谷歌官方提供的intent:預(yù)發(fā), 如果喚醒失敗,則會跳轉(zhuǎn)到谷歌的應(yīng)用市場。語法與URL Schemes及其相似,相當(dāng)于谷歌定制版的URL Schemes,也沒用過,就不多說。
IOS Universal Link
-
簡介
Universal Link是在iOS9引入的新功能,通過傳統(tǒng)的HTTP鏈接就可以喚醒a(bǔ)pp,如果用戶沒有安裝APP,則會跳轉(zhuǎn)到該鏈接對應(yīng)的頁面,而且在喚醒a(bǔ)pp的時候沒有彈框提示哦。可以說是解決了URL Schemes的大部分問題。 -
原理及流程
-
App開發(fā)人員去配置中心配置
Associated Domain配置一個支持https的域名,比如app-support.test.com- 然后app-support.test.com/apple-app-site-association或者app-support.test.com/apple-app-site-association/.well-known/apple-app-site-association要返回app的teamId,bundleId,paths信息.router.get('/apple-app-site-association, (req, res) => { const data = { applinks: { apps: [], details: [ { appID: 'teamId.bundleId', paths: ['*'] } ] } }; res.set('Content-Type', 'text/html'); res.send(JSON.stringify(data)); }); 然后APP安裝后首次打開,如果
Associated Domain配置了的話,就會去請求app-support.test.com/apple-app-site-association。系統(tǒng)會根據(jù)返回的teamId,bundleId,paths知道當(dāng)打開app-support.test.com下的哪些路徑的時候喚醒對應(yīng)的app,比如paths=*的話,就是打開app-support.test.com下的任意路徑都會喚醒a(bǔ)ppapp那邊會收到對應(yīng)的路徑,然后要根據(jù)
path寫邏輯跳轉(zhuǎn)到對應(yīng)的功能
-
-
如何驗證配置成功
在備忘錄中輸入配置好的鏈接,直接點開這個鏈接(
https://app-support.test.com),配置好的話會直接跳到app, 或者長按,彈出菜單中會提示在xxx中打開- 在safari中。
三、常見喚醒媒介
- iframe
ifr = document.createElement("iframe");
ifr.setAttribute("src", "wrjk://com.eko123"); /***打開app的協(xié)議,有an同事提供***/
ifr.style.display = "none";
document.body.appendChild(ifr);
iframe方案的喚起原理是: 程序切換到后臺時,計時器會被推遲(計時器不準(zhǔn)的又一種情況)。如果app被喚醒那么網(wǎng)頁必然就進(jìn)入了后臺,如果用戶從app切回來,那么時間一般會超過2s;若app沒有被喚起,那么網(wǎng)頁不會進(jìn)入后臺,setTimeout基本準(zhǔn)時觸發(fā),那么時間不會超過2s。
在未安裝 app 的情況下,不會去跳轉(zhuǎn)錯誤頁面。但在各個系統(tǒng)及應(yīng)用中兼容性比較多。例如:ios9+ 禁止掉了iframe方式。
-
a 標(biāo)簽
a = document.createElement("a"); a.setAttribute("href", "wrjk://com.eko123"); /***打開app的協(xié)議,有an同事提供***/ a.style.display = "none"; document.body.appendChild(a);a標(biāo)簽如果目標(biāo)scheme錯誤,即應(yīng)用不存在也不會報錯
-
window.location跳轉(zhuǎn)
window.location.href = "wrjk://com.eko123";兼容性:URL Scheme 在 ios 9+ 上諸如 safari、UC、QQ瀏覽器中, iframe 均無法成功喚起 APP,只能通過 window.location 才能成功喚端。(本人沒測試過,摘自別人的帖子)
三種喚醒媒介對比
某篇博文中對三種喚起方式進(jìn)行了測試
。X表示喚起失敗,√表示喚起成功
。紅色標(biāo)記表示進(jìn)入頁面直接喚起,綠色表示人工事件操作后喚起
。ios測試機(jī):iphone 6p;android測試機(jī):小米1s
-
iframe喚起app測試結(jié)果
14657587-3a6cae2885fc2d63.jpg -
a標(biāo)簽喚起app測試結(jié)果
14657587-e4d90cde88ffa81a.jpg -
iframe和window.location.href喚起對比
14657587-5dd8cd13b99d7511.jpg -
iframe、window.location.href和a標(biāo)簽喚起三者對比
14657587-5abb787d252c9169.jpg
四、總結(jié)
- 對于ios來說,location.href跳轉(zhuǎn)更合適,因為這種方式可以在Safari中成功喚起app。Safari作為iphone默認(rèn)瀏覽器其重要性就不用多說了。
- 對于Android來說,在進(jìn)入頁面直接喚起的情況下,iframe和location.href是一樣的,但是如果是事件驅(qū)動的喚起,iframe喚起的表現(xiàn)比location.href要更好一點。
- 通過測試可以發(fā)現(xiàn),進(jìn)入頁面直接喚起和事件驅(qū)動的喚起,對于很多瀏覽器,兩者的表現(xiàn)是不同的,簡單來說,直接喚起的失敗更多。
- 以上測試可能隨時間已經(jīng)有出入和變化,僅供參考。
五、使用中常見問題及解決方案
-
問題:可能會被app禁掉,比如微信,qq等
解決:通常會檢測打開的app環(huán)境,如果是微信,qq等環(huán)境,提示用戶瀏覽器內(nèi)打開。
-
問題:ios9+ 禁止掉了
iframe方式。解決:通常會檢測ios的版本,ios9+不使用
iframe方式,或直接用window.location.href. -
問題:h5無法感知是否喚醒成功.
解決:一段時間之后自動跳轉(zhuǎn)下載頁,或者是依賴setTimeout在瀏覽器進(jìn)入后臺后進(jìn)程切換導(dǎo)致的時間延遲判斷。
-
問題:大部分瀏覽器需要用戶手動觸發(fā)鏈接,js自動觸發(fā)無效。
解決:瀏覽器機(jī)制,可以設(shè)置彈框不在顯示。
實際應(yīng)用
之前已經(jīng)介紹了要實現(xiàn)的需求,讓我們一步一步來吧。我應(yīng)用的是URL Schemes喚起方法,其實最完美的方案是IOS Universal Link,因為這個方法需要app那邊配置很多東西,配置好后給前端提供一個地址調(diào)用。首先要獲取訪問設(shè)備的信息,代碼如下:
let globalData = {
isMobile: false,//是否為移動端
openIn: '',//頁面打開的app
openInType: '',//移動端設(shè)備的類型 ios an
isHW: false,//是否為華為瀏覽器
}
var browser = {
versions: function () {
var u = navigator.userAgent, app = navigator.appVersion;
return { //移動終端瀏覽器版本信息
trident: u.indexOf('Trident') > -1, //IE內(nèi)核
presto: u.indexOf('Presto') > -1, //opera內(nèi)核
webKit: u.indexOf('AppleWebKit') > -1, //蘋果、谷歌內(nèi)核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐內(nèi)核
mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否為移動終端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios終端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android終端或uc瀏覽器
iPhone: u.indexOf('iPhone') > -1, //是否為iPhone或者QQHD瀏覽器
iPad: u.indexOf('iPad') > -1, //是否iPad
webApp: u.indexOf('safari') == -1, //是否web應(yīng)該程序,沒有頭部與底部
//DF:u.indexOf('DF'),//是否是mmc app端
};
}(),
language: (navigator.browserLanguage || navigator.language).toLowerCase(),
}
if (browser.versions.mobile) {//判斷是否是移動設(shè)備打開。browser代碼在下面
globalData.isMobile = true;
var ua = navigator.userAgent.toLowerCase();//獲取判斷用的對象
console.log(ua + '=============ua對象');
if (ua.match(/MicroMessenger/i) == "micromessenger") {
//在微信中打開
globalData.openIn = 'weixin';
}
if (ua.match(/WeiBo/i) == "weibo") {
//在新浪微博客戶端打開
globalData.openIn = 'weibo';
}
if (ua.match(/ qq\//i) == " qq/") {
//在QQ空間打開
globalData.openIn = 'qq';
}
if (ua.match(/huawei/i) == 'huawei') {
globalData.isHW = true;
}
if (browser.versions.ios) {
//是否在IOS瀏覽器打開
globalData.openInType = 'ios';
}
if (browser.versions.android) {
//是否在安卓瀏覽器打開
globalData.openInType = 'android';
}
} else {
//否則就是PC瀏覽器打開
globalData.isMobile = false;
}
我定義了一個全局對象globalData,用來存儲設(shè)備信息,有個屬性isHW,是因為華為瀏覽器和其他安卓設(shè)備又有些不同,故區(qū)分出來單獨處理。
此上是設(shè)備判斷的邏輯,能夠得到是pc端打開還是移動端,移動端打開app是什么app(例如:微信,微博,qq等),移動端設(shè)備的類型是 ios 還是 an,以及是否是華為手機(jī)。
下面就來寫調(diào)起的程序了。
//喚起app或跳轉(zhuǎn)下載頁
function applyApp(){
if (globalData.isMobile && globalData.openInType == "android") {
//判斷是否是會屏蔽下載鏈接的app打開
if (
globalData.openIn == "weixin" ||
globalData.openIn == "weibo" ||
globalData.openIn == "qq"
) {
//Toast("請將鏈接復(fù)制到瀏覽器中打開");
$('.mask').show();
return;
} else {
if (globalData.isHW) {
window.location.href = "wrjk://com.eko123.manmachine/startapp";
} else {
ifr = document.createElement("iframe");
ifr.setAttribute("src", "wrjk://com.ekao123.manmachine/startapp"); /***打開app的協(xié)議,有an同事提供***/
ifr.style.display = "none";
document.body.appendChild(ifr);
}
timer = setTimeout(function () {
location.;
}, 5000);
}
}
if (globalData.isMobile && globalData.openInType == "ios") {
if (
globalData.openIn == "weixin" ||
globalData.openIn == "weibo" ||
globalData.openIn == "qq"
) {
//Toast("請將鏈接復(fù)制到瀏覽器中打開");
$('.mask').show()
return;
} else {
var loadDateTime = new Date();
window.location = "com.ht.yiqikao://";//schema鏈接或者universal link
window.setTimeout(function() { //如果沒有安裝app,便會執(zhí)行setTimeout跳轉(zhuǎn)下載頁
var timeOutDateTime = new Date();
if (timeOutDateTime - loadDateTime < 2000) {
window.location = "https://itunes.apple.com/cn/app/id1422958471?l=zh&ls=1&mt=8"; //ios下載地址
} else {
window.close();
}
}, 2000);
}
}
}
至此,喚起app的需求就完成了,僅供大家參考,若有更好的方法,歡迎留言交流。
本文部分內(nèi)容參考自:http://m.itdecent.cn/p/500f4be528e3



