上一篇文章介绍了URL Scheme拦截和JSBridge注入,这篇文章主要介绍如何在使用上述方式通信时获得回调。
以下示例通过JSBridge的方式来演示双端通信回调函数的使用。Scheme方式与其相同,只是将传递到JSBridge中的参数拼接到URL Scheme的参数中去,在Native端通过截取到对应方法后的参数,来做相应的回调。
回调逻辑介绍
以Android端调用Native输入框为例,将Native端输入的message通过回调函数传递回Android端。
- web调用Native输入框
- 用户在Native输入框输入message
- Native端拿到用户输入的message传递给web端
- web端接收到message执行回调函数
因此在整个流程中要明确以下两个点:
- 回调函数通信了两次,第一次是web携带回调函数名称调用Native,第二次是Native携带返回值调用web;
- 回调函数位于web端,是用来处理Native端返回的message
web端
// 调用Native输入框函数
getMessage() {
// 调用前先将回调函数挂载到window上
window.setMessage = message => console.log('--setMessage--', message);
// 调用JSBridge对应方法
NativeBridge. getNativeEditTextValue("setMessage");
}
Andriod端
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");
// 注入的JSBridge对象
class NativeBridge {
private Context ctx;
NativeBridge(Context ctx) {
this.ctx = ctx;
}
@JavascriptInterface
public void getNativeEditTextValue(int callbackName) {
MainActivity mainActivity = (MainActivity)ctx;
String value = mainActivity.editText.getText().toString();
// 调用web上面的回调方法,并将value传递进去
String jsCode = "window." + callbackName + "(" + value + ")");
mainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mainActivity.webView.evaluateJavascript(jsCode, null);
}
});
}
}
回调的问题
上面的方法是回调函数最简单的方式,但是有一个弊端就是在全局对象window上定义了回调函数名称。
很显然这样做可能造成与window上其他函数重名覆盖,污染全局变量的问题。
解决办法
解决办法就是将回调函数放入到一个专门处理回调的全局对象
中,通过一个id或者名称去标识他。虽然在两端通信时仍然传递的是回调名称或id,但是在传递之前多了一个将回调保存到一个全局对象中的过程,在传递之后多了一个从全局对象中取出这个回调的过程。
web端
let callbackId = 0;
// 保存回调函数的全局对象
let callbackMap = {};
window.JSSDK = {
// 将回调保存到一个全局对象中
storeCallback: callback => {
// 使用名称存储:setMessage
// callbackMap['setMessage'] = callback;
// window.NativeBridge.getNativeEditTextValue("setMessage");
// 使用id存储
callbackId++;
callbackMap[callbackId] = callback;
window.NativeBridge.getNativeEditTextValue(callbackId);
},
// 从全局对象中取出这个回调
takeCallback: (callbackName, message) => {
if(callbackMap[callbackName]) {
callbackMap[callbackName](message);
} else {
// 回调函数不存在的处理情况
}
}
}
Native端
@JavascriptInterface
public void getNativeEditTextValue(int callbackName) {
MainActivity mainActivity = (MainActivity)ctx;
String value =mainActivity.editText.getText().toString();
// 调用web上从全局对象中取出这个回调的方法,并将value传递进去
String jsCode = "window.JSSDK.takeCallback(%s, '%s')", callbackName, value);
mainActivity.runOnUiThread(new Runnable() {
@Override public void run() {
mainActivity.webView.evaluateJavascript(jsCode, null);
}
});
}