为了统一android端和ios端调用原生jsbridge方法统一,且web端不需要使用callback方式接收回调,特封装了以下js工具类:
// 全局回调管理器
window.CallbackManager = {
callbacks: new Map(),
registerCallback: function (callbackId, callback) {
this.callbacks.set(callbackId, callback);
},
unregisterCallback: function (callbackId) {
this.callbacks.delete(callbackId);
},
invokeCallback: function (callbackId, result) {
const callback = this.callbacks.get(callbackId);
console.log("查看回调数据:"+callbackId+"***"+result);
if (callback) {
callback(result);
console.log("执行了回调");
this.unregisterCallback(callbackId);
}
},
invokeCallbackForIOS: function (result) {
let decodedResponse = decodeURIComponent(result);
const jsonObject = JSON.parse(decodedResponse);
let callbackId = jsonObject.callbackId
const callback = this.callbacks.get(callbackId);
console.log("查看回调数据:"+callbackId+"***"+result);
if (callback) {
callback(jsonObject.result);
console.log("执行了回调");
this.unregisterCallback(callbackId);
}
}
};
window.mobileNativeMethod= {
callHandler: function (methodName, params, callback) {
let userAgent = navigator.userAgent;
if (/iPad|iPhone|iPod/.test(userAgent)) {
// iOS平台调用原生方法
let callbackId = Math.random().toString(36).substring(7);
let jsonParams = JSON.parse(params)
jsonParams.methodName = methodName
jsonParams.callbackId = callbackId
window.webkit.messageHandlers.mobileNativeMethod.postMessage(JSON.stringify(jsonParams));
CallbackManager.registerCallback(callbackId, callback);
} else if (userAgent.indexOf('Android') > -1) {
// Android平台调用原生方法
if (window.androidJavascript) {
console.log("查看调用:"+methodName+"***"+params)
let callbackId = Math.random().toString(36).substring(7);
window.androidJavascript.callHandler(methodName, params, callbackId);
CallbackManager.registerCallback(callbackId, callback);
}
} else {
console.warn('Unsupported platform');
}
}
};
在web页面移动端统一使用 **window.mobileNativeMethod.callBack(methodName,params, callback)**方法调用原生实现的方法。
methodName是字符串方法名,params是Map参数,如果需要接收回调需要实现callback,如果不需要回调可以省略回调。
此处以android原生端实现的jsbridge为例,示例实现如下:
public class MyWebViewJavaScript {
private final String TAG = "MyWebViewJavaScript";
private TextView mTvtitle;
private SetWebTitle mSetWebTitle;
List mHandles;
public Map<String, String> callBackIdStack;
private StWebView mWebView;
private Handler mHandler;
//响应数据
private String response = "";
public interface SetWebTitle {
void toSetWebTitle(String title);
void toHideTitleBar(boolean isHide);
}
public WebViewJavaScript(StWebView webview, SetWebTitle setWebTitle) {
EventBusUtil.register(this);
this.mWebView = webview;
callBackIdStack = new HashMap<>();
mHandler = new Handler(Looper.getMainLooper());
this.mSetWebTitle = setWebTitle;
mHandles = new ArrayList<>();
//缓存相关的js通讯渠道
mHandles.add(new CacheChannelImpl());
//公共的js通讯渠道
mHandles.add(new CommonChannelImpl(mWebView.getContext()));
}
@JavascriptInterface
public void callHandler(String handleName, String jsonArgs, String callbackId) {
if (mWebView == null) {
return;
}
//设置web页面标题
if (handleName.equals("setAppBarTitle")) {
JSONObject jsonObject = JSON.parseObject(jsonArgs);
String title = jsonObject.getString("title");
if (null != mSetWebTitle) {
mSetWebTitle.toSetWebTitle(title);
} else {
Log.d(TAG, "Interface SetWebTitle is null");
}
return;
}
//是否隐藏标题栏
if (handleName.equals("hideTitleBar")) {
JSONObject jsonObject = JSON.parseObject(jsonArgs);
boolean isHide = jsonObject.getBoolean("isHide");
if (null != mSetWebTitle) {
mSetWebTitle.toHideTitleBar(isHide);
} else {
Log.d(TAG, "Interface SetWebTitle is null");
}
return;
}
boolean hasMethod = false;
for (IJsCallHandle handle : mHandles) {
if (handle.handle(handleName)) {
if (handle instanceof CacheChannelImpl) {
handle.run(mWebView, jsonArgs, callbackId, callBackIdStack);
} else {
handle.run(mWebView, jsonArgs);
callBackIdStack.put(handleName, callbackId);
}
hasMethod = true;
break;
}
}
if (!hasMethod) {
//未实现方法 handleName
mHandler.post(new Runnable() {
@Override
public void run() {
Gson gson = new GsonBuilder().disableHtmlEscaping() // 禁用 HTML 转义
.create();
HashMap<String, Object> mmap = new HashMap<>();
mmap.put("result", "");
mmap.put("code", "error");
mmap.put("message", String.format("未实现方法%s", handleName));
mmap.put("methodName", handleName);
response = gson.toJson(mmap);
String jsFunctionCall = "javascript:CallbackManager.invokeCallback('" + callbackId + "', '" + response + "')";
mWebView.loadUrl(jsFunctionCall);
callBackIdStack.remove(handleName);
}
});
}
}
}
CommonChannelImpl的实现示例:
public class CommonChannelImpl implements IJsCallHandle {
private Map<String, Boolean> methods = new HashMap<String, Boolean>();
private String mHandleName = "";
private Context mContext;
public CommonChannelImpl(Context context) {
this.mContext = context;
//权限请求
methods.put("requestPermission", false);
//获取应用基本信息
methods.put("getAppInfo", false);
//web页面导航返回
methods.put("NavigatorPop", false);
//打开浏览器
methods.put("openBrowser", false);
//跳转页面
methods.put("jumpPage", false);
//打电话
methods.put("callPhone", false);
}
@Override
public boolean handle(String handleName) {
if (methods.containsKey(handleName)) {
mHandleName = handleName;
methods.put(handleName, true);
return true;
}
return false;
}
@Override
public void run(StWebView webView, String jsonArgs) {
long requestCode = 0;
if (!TextUtils.isEmpty(jsonArgs)) {
JSONObject jobj = JSON.parseObject(jsonArgs);
if (jobj.containsKey("requestCode")) {
requestCode = jobj.getLong("requestCode");
}
}
switch (mHandleName) {
case "requestPermission":
EventMessage<Object> eventMessage1 = new EventMessage<>(0, requestCode, "module-permission", mHandleName, jsonArgs);
EventBusUtil.sendEvent(eventMessage1);
break;
case "getAppInfo":
EventMessage<Object> eventMessage = new EventMessage<>(0, requestCode, "component-phoneinfo", mHandleName, jsonArgs);
EventBusUtil.sendEvent(eventMessage);
break;
case "NavigatorPop":
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
if (webView.canGoBack()) {
webView.goBack();
} else {
if (mContext instanceof AppCompatActivity) {
((AppCompatActivity) mContext).finish();
}
}
}
});
break;
case "openBrowser":
JSONObject jsonObject = JSON.parseObject(jsonArgs);
if (jsonObject != null) {
String url = jsonObject.getString("url");
if (!url.contains("http")) {
url = "http://" + url;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
mContext.startActivity(intent);
}
break;
case "jumpPage":
JSONObject jsonObject1 = JSON.parseObject(jsonArgs);
String pageUrl = jsonObject1.getString("pageUrl");
JSONObject params = jsonObject1.getJSONObject("params");
Map<String, Object> map = new HashMap<>();
for (String key : params.keySet()) {
map.put(key, params.get(key));
}
ARouterHelper.navigateToFlutterPage(pageUrl, map);
break;
case "callPhone":
JSONObject jsonObject2 = JSON.parseObject(jsonArgs);
String number = jsonObject2.getString("number");
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + number));
mContext.startActivity(intent);
break;
default:
break;
}
}
}