假如生活欺骗了你,假如工作到处不顺,请放松心态,提升自己,终有一天你会适应,然后。。。。。。。。。。。。。。。
本篇是面试小问题专栏的开篇文章,由于是面试小问题的解答,所以所有的文章都会力求把问题说明白的同时精简字数,可能有理解不到位的地方后续会慢慢完善。
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简单介绍就到这里。