Hybrid開發中,web頁面每每會跟native進行交互,而JSBridge就是web頁面和native進行通訊的橋樑,經過JSBridge能夠實現web調用native的方法,native能夠經過
webview.loadUrl
之類的方法,將javascript:xxx
代碼放在頁面執行,這有點相似在瀏覽器地址欄直接輸入:javascript:xxx
javascript
本文較長,先把目錄列出來:html
web和native進行通訊,方法有不少,接下來一一列舉一下JSBridge的多種形式,及其利弊。java
JSInterface是安卓4.2-官方推薦的解決方案,原理是經過WebView提供的addJavascriptInterface
方法給瀏覽器window
注入一個命名空間,而後給Web增長一些能夠操做Java的反射。node
// Android java代碼
mWebView.addJavascriptInterface(new Class(), 'android');
public class Class(){
@JavascriptInterface
public void method(){
}
}
// js 代碼
window.android.method();複製代碼
JSInterface在4.2以前的版本均可以,可是存在嚴重的安全隱患,容易被利用提權,從而調用各類Java的類和權限,甚至頁面能夠掛馬。在咱們實際產品(手機百度)開始階段,用過這個方法,不過如今已經不使用了。android
這個方法主要是經過修改原來瀏覽器的window
某些方法,而後攔截固定規則的參數,而後分發給Java對應的方法去處理。這裏經常使用的是如下四個方法:ios
onJsAlert
監聽onJsConfirm
監聽onConsoleMessage
監聽onJsPrompt
監聽prompt簡單舉例說明,Web頁面經過調用prompt()
方法,安卓客戶端經過監聽onJsPrompt
事件,攔截傳入的參數,若是參數符合必定協議規範,那麼就解析參數,扔給後續的Java去處理。這種協議規範,最好是跟iOS的協議規範同樣,這樣跨端調起協議是一致的,但具體實現不同而已。好比:hybrid://action?arg1=1
這樣的協議,而其餘格式的prompt
參數,是不會監聽的,即除了hybrid://action?arg1=1
這樣的規範協議,prompt
仍是原來的prompt
。web
這四個方法也是各有利弊,好比:json
alert
/console.log
是調試最經常使用的,若是你要看看協議是否是寫錯了,可是傳入協議卻被攔截了。。confirm
和prompt
都帶返回值,prompt
是四個裏面惟一能夠自定義返回值,能夠作同步的交互,要比寫各類回調更「順」,可是一旦串行調用了,就要罵爹了prompt
是咱們目前安卓用的比較多的JSBridge解決方案。api
這個叫法不是特別貼切,scheme是URI的一種格式,上文提到的hybrid://action?arg1=1
就是一個scheme協議,這裏說的scheme(或者schema)泛指安卓和iOS的schema協議,由於它通用。瀏覽器
安卓和iOS均可以經過攔截跳頁URL請求,而後解析這個scheme協議,符合約定規則的就扔個Native的方法處理。安卓和iOS分別用到攔截URL請求的方法是:
上文介紹到的JSBridge是在APP內的Web頁面跟APP進行交換,還有一種特別多的需求,就是在APP外(瀏覽器、微信等)調起APP本身,給APP進行導流。這時候就要用到APP的喚起技術。這裏有一下幾種方法:
intent格式示例以下:
intent:
HOST/URI-path // Optional host
#Intent;
package=[string];
action=[string];
category=[string];
component=[string];
scheme=[string];
S.xxx=xxx
end;複製代碼
由於Intent不只僅是調起APP,而是安卓客戶端內部模塊通訊也會用,因此權限很大,通常瀏覽器都給封掉了,💔
這是一個黑科技😈,早前安卓容許在本地啓動一個本地server,這個server是在後臺守候的,經過這個localserver均可以進行各類需求:app間通訊、app調起、收集數據、基礎服務。百度的moplus就是這樣的一個localserver。
舉例說明:啓動一個本地server,端口號是:8888
,那麼在手機上,網頁就能夠經過:http://127.0.0.1:8888
訪問這個server,server接收到請求就能夠進行一些native的操做,對於須要回調數據的,就經過返回請求內容來執行,好比:
$.get('http://127.0.0.1:8888/getGeoLocation?callback=cbname')
window.cbname&&cbname({xxx})
給頁面返回定位數據若是控制很差權限,由於localserver是一直後臺守候的,容易被利用,好比提權獲取通信錄、甚至給通信錄發短信、容易形成蠕蟲攻擊,感興趣的能夠搜下moplus的文章。另外安卓各類安全軟件,都會清理內存和後臺程序,很容易被幹掉進程。瀏覽器也會封殺本地server調起,遇見127.0.0.1的請求就直接攔截。
這三個是官方推薦的調起方法,調起協議格式也是能夠統一的,好比前文提到的hybrid://action?arg1=xxx
這類scheme協議就是。這樣能夠統一安卓和iOS調起和JSBridge通訊。
其實簡單來講,這三個出發點是想給應用作分發,可是若是用戶手機沒有安裝這個APP,那麼就調起失敗,這時候直接無論不問,確定體驗很差,並且浪費了點擊資源,那麼作成分發吧!將調起協議作成一個調起頁面,放到一個域名下,點擊這個URL就能夠打開這個頁面,頁面執行代碼調起APP,若是調起失敗就展示APP的介紹,作分發。
Universal links / Deep link / Applink,就是這樣的一個過程,經過這個域名受權,把URL分發給APP進行處理,惟一不一樣的是:若是用戶安裝了APP,那麼就不用打開這個分發頁面了。
iOS 9新出的一個功能,須要在App內聲明一個https域名(ul.test.com),而後在該網站根目錄放置apple-app-site-association文件,文件指明瞭轉發規則,例如:
{
"applinks": {
"apps": [],
"details": [
{
"appID": 「xxx.com.baidu.SomeApp」,
"paths": ["*"]
}
]
}
}複製代碼
當APP安裝成功以後,會下載這個文件,明確知道碰見ul.test.com
的域名的URL時候,會把這個URL扔給你的APP,讓你去解析,APP拿到這個URL就能夠解析出來須要作什麼事情。
Universal Link是iOS 9+的底層實現,因此在任何地方均可以直接調起APP,不受微信這類封閉APP的限制。
Deep link 是安卓一開始推出的,主要用於搜索調起APP,後來推出 Applink,實際是Deep link的升級版。
這裏須要提到微信的APPlink,畢竟微信做爲SuperApp,是很大的分發資源,微信有本身的分發方法,安卓內能夠申請微信的APPlink,跟Universal link同樣,也是一個域名下面的URL,符合必定規則就由微信(ios是底層系統)扔個對應的域名APP進行解析。
在頁面的head中添加下面meta,在Safari瀏覽器中就會出現下面的banner
<meta name="apple-itunes-app" content="app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL">複製代碼
在APP內JSBridge能夠實現Web和Native的通訊,可是若是APP打開一個惡意的頁面,頁面能夠任意調用JSBridge方法,獲取各類隱私的數據,就會引發安全問題。
JSBridge的安全有兩個方法:經過Native進行白名單配置,經過Server雲端受權。Server的雲端受權這塊,放到後續JSSDK的設計部分進行詳細講解。本文主要說下經過Native的方式來控制JSBridge的安全。
假設JSBridge的協議格式以下:
hybrid://action/method?arg1=xxx&arg2=xxx複製代碼
能夠經過下面方式進行安全設置:
*
,hao123.com可使用:hybrid://hao123/*
介紹了這麼多,什麼是最佳實踐的JSBridge呢?結合文章內容,要求JSBridge作到如下幾點:
綜合上文介紹的內容,JSBridge的最佳實踐是:
hybrid://action/method?arg1=xxx&arg2=xxx
先貼個URL scheme的圖片,理解下URL的組成部分:
約定咱們的規範以下:
yourappscheme://module/action?arg1=x&arg2=x&ios_version=xxx&andr_version=xxx&upgrade=1/0&callback=xxx&sendlog=1/0複製代碼
?
後面是querystring,這裏預約了幾個特殊的參數:
ios_version/andr_version
:非必須,iOS和安卓的最小版本,即本協議從哪一個版本開始支持的,低版本不支持則忽略,配合upgrade使用進行APP升級舉例:
# 帳號相關
## 打開用戶我的主頁
fb://account/userprofile?id=xxx
## 打開登陸界面
fb://account/login?callback=xxx
# 工具類
## 獲取定位
fb://utils/getgeolocation?callback=xx複製代碼
當native操做成功以後,會將處理結束後的結果或者數據經過callback
回調傳給Web,固然有成果就又失敗,callback
的參數設計有兩種方式:
即下面的回調方法格式:
function callback(error, data){
if(error){
throw error
}
console.log(data)
}複製代碼
即回調方法只接收一個JSON對象,JSON格式以下:
{
error_code: 0,
data: {}
}複製代碼
作APP開發常常會碰見下面的問題:
scheme的querystring部分由 ios_version/andr_version
和upgrade這三個成對的參數,能夠解決升級問題,sendlog解決日誌統計問題。
ios_version/andr_version
:是標示該協議的最低支持版本,若是低於這個版本可能由於功能並未實現而能識別。簡單封裝下JSBridge調用的方法,參數以下:
具體代碼以下
function invoke (module, action, args, callback) {
let scheme = `yourappscheme://${module}/${action}?`
if (isFunction(args)) {
callback = args
args = null
}
// 處理下參數
if (isString(args)) {
scheme += args
} else if (isObject(args)) {
each(args, (k, v) => {
if (isObject(v) || isArray(v)) {
v = JSON.stringify(v)
}
scheme += `${k}=${v}`
})
}
// callback獨立傳,方便全局函數名命名
if (isFunction(callback)) {
var funcName = '_jsbridge_cb_' + getId()
window[funcName] = function () {
callback.apply(window, ([]).slice.call(arguments, 0))
}
scheme += (!~scheme.indexOf('?') ? '&' : '?') + `callback=${funcName}`
}
if (os.ios && versionCompare(os.version, '9.0') >= 0) {
window.location.href = scheme
} else {
var $node = document.createElement('iframe')
$node.style.display = 'none'
$node.src = scheme
var body = document.body || document.getElementsByTagName('body')[0]
body.appendChild($node)
setTimeout(function () {
body.removeChild($node)
$node = null
}, 10)
}
}複製代碼
今天寫的有點多,介紹了JSBridge經常使用的方法,而後介紹了APP外如何喚起APP,還介紹了scheme協議,最後比較了優缺點,作個最佳實踐。但願有用~
--eof--
@三水清
未經容許,請勿轉載
本文使用MPEditor(js8.in/mpeditor)編輯…
感受有用,歡迎關注個人公衆號