假如生活欺騙了你,假如工作到處不順,請放松心态,提升自己,終有一天你會适應,然後。。。。。。。。。。。。。。。
本篇是面試小問題專欄的開篇文章,由于是面試小問題的解答,是以所有的文章都會力求把問題說明白的同時精簡字數,可能有了解不到位的地方後續會慢慢完善。
h5可以作為移動端跨平台的一種方式,其他方式還有rnjs,flutter,weekx等,android或者ios為了實作和js的互動都提供了原生方法,但都存在一定的問題,這時jsbridge就誕生了。
android原生和js互動方式,調用js,loadurl通用,evaluateJavascript支援回調但都涉及版本問題,js調用原生不同版本又設定到安全問題,雖然新版利用@JavascriptInterface 解決了安全問題但現實中依然有很多低版本。
jsbridge 優點說明:
1 統一android ,ios兩邊的調用方式,使綁定和調用函數接口統一,
2 完美解決安全性問題和回調函數問題。
原理說明:
互動通道:
要實作原生和js互動首先需要一條雙方互動的通道,一般的jsbridge都是利用攔截prompt函數,webView端攔截了js端發送的prompt函數,可以擷取上面的文本。原生執行js還是利用原來的loadurl。
重寫webView和 WebChromeClient,攔截prompt。
public static class WebChromeClientEx extends WebChromeClient {
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
Logger.d("JsBridge","onJsPrompt: " + message);
try {
//根據協定進行解析
JSONObject jsonObject = new JSONObject(message);
if (JavascriptBridge.matchBridgeProtocol(jsonObject)) {
result.confirm("prompt ok");
JavascriptBridge.onGetDataFromJs(view, jsonObject);
} else {
result.confirm("data does not match bridge protocol");
}
} catch (Exception e) {
e.printStackTrace();
result.confirm("exception");
}
return true;
}
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
if (view instanceof BaseWebView) {
SimpleToastHelper.showShortToast(view.getContext(), message);
}
result.confirm();
return true;
}
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if (view instanceof BaseWebView) {
SimpleToastHelper.showShortToast(view.getContext(), message);
}
result.confirm();
return true;
}
}
定義協定:
js調用原生,會通過調用prompt函數,webView擷取到prompt傳遞的文本(協定),根據自定義協定解析文本,執行特定原生函數。
js端如何調用原生的函數和回調如何處理:
native(原生)的函數A會被js調用,首先A和實作會被存儲到本地的map中,然後native端回調用js端寫好的注冊函數,在對應的js端生成一個代表原生函數A的js函數B(其實兩者之間沒有必然聯系,不想系統提供的js調用native的方式,沒有注冊對象,這樣就保證了安全性),然後js調用想調用原生A函數時,就調用js函數B,利用prompt把調用原生A的協定傳過去,onjsprompt攔截協定,調用本地A函數。
是以jsbridge都會提供H5端注冊代碼用于生成js的javascriptBridge對象,類似
(function () {
if (window.bridge) {
return;
}
window.bridge = {};
var messages = {};
var call_id = 0;
native函數需要調用register函數,在h5端生成對應的函數
bridge.register = function (name) {
if (this[name]) {
return;
}
this[name] = function (params, callback) {
invokeNative(name, params, callback);
};
};
bridge.obtain = function (id) {
return JSON.stringify(messages[id].params);
};
bridge.callback = function (id, data) {
var message = messages[id];
if (!message) {
return;
}
if (message.callback) {
message.callback(JSON.parse(data));
}
delete messages[id];
};
最終實作調用原生的方法 prompt
function invokeNative (name, params, callback) {
call_id ++;
messages[call_id] = {'params' : params, 'callback' : callback};
if (navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Adr') > -1) {
prompt(JSON.stringify({'cmd':name, 'id':call_id, 'params':params}), '');
} else {
var iframe = document.createElement('iframe');
iframe.src = 'bridge://invoke.native.method?handler=' + name + '&id=' + call_id;
iframe.style.display = 'none';
document.documentElement.appendChild(iframe);
setTimeout(function () { document.documentElement.removeChild(iframe); }, 0);
}
}
})();
原生調用js如何實作回調:
可以Native根據協定生成js對象,webView加載網頁時将本地js注入到網頁中,原生調用注入js中的函數,js端接收調用首先調用js函數,然後根據協定調用原生端的對調函數。
jsbridge簡單介紹就到這裡。