为了统一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.callbackIdconst 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 = methodNamejsonParams.callbackId = callbackIdwindow.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) {//未实现方法 handleNamemHandler.post(new Runnable() {@Overridepublic 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() {@Overridepublic 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;}}
}