【问】native和js怎么交互?
【答】
- 概念说明:HybridAPP开发中常遇到native与webview中h5(即js)需要交互的问题。如js想要调用native拍照、图片上传、支付、页面分享等;native想要发送相册图片给js等。为了解决这两者间的通信问题,就有了JsBridge(需要注意的是,jsBridge更多的是指一种形式,一种思想,而不是一种具体的技术方案),我们需要实现它。
-
jsBridge实现:
2.1 js调用native:主要有两种方式,注入API和拦截URL Scheme
(1)注入API:该方式的主要原理是,通过Webview提供的接口,向js的context(window)中注入对象或者方法,让js调用时,直接执行相应native代码,达到js调用native的目的。
ios端实例:
Android端实例://IOS UIwebview代码 JSContext *context = [uiWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; context[@"postBridgeMessage"] = ^(NSArray<NSArray *> *calls) { // Native 逻辑 }; //js代码 window.postBridgeMessage(message);
//Android 代码 public class JavaScriptInterfaceDemoActivity extends Activity { private WebView Wv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Wv = (WebView)findViewById(R.id.webView); final JavaScriptInterface myJavaScriptInterface = new JavaScriptInterface(this); Wv.getSettings().setJavaScriptEnabled(true); Wv.addJavascriptInterface(myJavaScriptInterface, "nativeBridge"); // TODO 显示 WebView } public class JavaScriptInterface { Context mContext; JavaScriptInterface(Context c) { mContext = c; } public void postMessage(String webMessage){ // Native 逻辑 } } } //注: //在 4.2 之前,Android 注入 JavaScript 对象的接口是 addJavascriptInterface,但是这个接口有漏洞,可以被不法分子利用,危害用户的安全,因此在 4.2 中引入新的接口 @JavascriptInterface(上面代码中使用的)来替代这个接口,解决安全问题。所以 Android 注入对对象的方式是 有兼容性问题的。(4.2 之前很多方案都采用拦截 prompt 的方式来实现) //js代码 window.nativeBridge.postMessage(message);
(2)拦截URL Scheme:该方式是拦截h5以某种方式(如iframe.src、alert等)发送的URL Scheme(比如test1://ab/url?a=2,具体拦截哪种特定请求需要两端协商好)请求,如以自定义协议“test1://ab”开头的都拦截下来,执行相应的native方法;其他开头的放行。
2.2 native调用js:其实就是直接调用挂载在context环境(window)中的对象或方法,执行拼接的js字符串。
ios端实例:
Android端实例://IOS UIwebview代码 result = [uiWebview stringByEvaluatingJavaScriptFromString:javaScriptString]; //或者IOS WKwebview代码 [wkWebView evaluateJavaScript:javaScriptString completionHandler:completionHandler];
2.3 相互调用时的callback实现:用一个自增的唯一 id,来标识并存储回调函数,并把此 id 以参数形式传递给 Native,而 Native 也以此 id 作为回溯的标识。这样,即可实现 Callback 回调逻辑。//Android代码 webView.evaluateJavascript(javaScriptString, new ValueCallback<String>() { @Override public void onReceiveValue(String value) { } });
- JsBridge代码实现:
(function () {
var id = 0,
callbacks = {},
registerFuncs = {};
window.JSBridge = {
// 调用 Native
invoke: function(bridgeName, callback, data) {
// 判断环境,获取不同的 nativeBridge
var thisId = id ++; // 获取唯一 id
callbacks[thisId] = callback; // 存储 Callback
nativeBridge.postMessage({
bridgeName: bridgeName,
data: data || {},
callbackId: thisId // 传到 Native 端
});
},
receiveMessage: function(msg) {
var bridgeName = msg.bridgeName,
data = msg.data || {},
callbackId = msg.callbackId, // Native 将 callbackId 原封不动传回
responstId = msg.responstId;
// 具体逻辑
// bridgeName 和 callbackId 不会同时存在
if (callbackId) {
if (callbacks[callbackId]) { // 找到相应句柄
callbacks[callbackId](msg.data); // 执行调用
}
} else if (bridgeName) {
if (registerFuncs[bridgeName]) { // 通过 bridgeName 找到句柄
var ret = {},
flag = false;
registerFuncs[bridgeName].forEach(function(callback) => {
callback(data, function(r) {
flag = true;
ret = Object.assign(ret, r);
});
});
if (flag) {
nativeBridge.postMessage({ // 回调 Native
responstId: responstId,
ret: ret
});
}
}
}
},
register: function(bridgeName, callback) {
if (!registerFuncs[bridgeName]) {
registerFuncs[bridgeName] = [];
}
registerFuncs[bridgeName].push(callback); // 存储回调
}
};
})()
-
JsBridge引入方式:
(1)由native端进行注入,直接执行JsBridge的全部代码
(2) 由js端执行JsBridge全部代码
参考1:移动混合开发中的JsBridge