android与js交互-jsbridge
16lz
2021-12-04
完整项目:https://github.com/snailycy/android_jsbridge
1.1 配置WebView
public void configWebView() { try { WebSettings settings = this.mWebView.getSettings(); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setDatabaseEnabled(true); settings.setBuiltInZoomControls(false); settings.setDomStorageEnabled(true); settings.setAppCacheEnabled(true); //设置localStorage存储路径 String localStorageDBPath = this.mWebView.getContext().getFilesDir().getAbsolutePath(); settings.setDatabasePath(localStorageDBPath); this.mWebView.setWebViewClient(new JSWebViewClient(this)); this.mWebView.setWebChromeClient(new JSWebChromeClient(this)); } catch (Exception e) { LogUtils.e(TAG, "configWebView error."); } }
1.2 安卓端拦截js的请求在WebChromeClient类中的onJsAlert方法中处理
注:如果是用addJavascriptInterface的方式接受js请求,那么在android 4.2系统以下版本有js注入漏洞(在4.2及以上系统时引入@JavascriptInterface可避免)
@Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { if (message.startsWith(JS_REQUEST_PREFIX)) { if (this.jsBridge == null) { result.cancel(); return true; } parseJSProtocol(message); result.cancel(); return true; } return super.onJsAlert(view, url, message, result); }
1.3 自定义JS 请求协议:myjsbridge:///request?class=指定调用的类名&method=指定调用的方法名¶ms=指定的参数&callId=指定的请求ID
解析时按照协议格式分别解析出类名,方法名,参数,callId
/** * 解析JS协议 * * @param message: myjsbridge:///request?class=指定调用的类名&method=指定调用的方法名¶ms=指定的参数&callId=指定的请求ID */ private void parseJSProtocol(String message) { String[] tokens = message.substring(JS_REQUEST_PREFIX.length()).split("&"); String target = null; String method = null; String params = null; long callID = -1; for (String token : tokens) { String[] pair = token.split("="); if (pair.length != 2) { continue; } try { String key = pair[0]; String value = Uri.decode(pair[1]); if (JS_REQUEST_CLASS_KEY.equals(key)) { target = value; } else if (JS_REQUEST_METHOD_KEY.equals(key)) { method = value; } else if (JS_REQUEST_PARAMETERS_KEY.equals(key)) { params = value; } else if (JS_REQUEST_CALL_ID_KEY.equals(key)) { callID = Long.parseLong(value); } } catch (Exception e) { // Ignores. } } if (target != null && method != null && callID >= 0) { this.jsBridge.requestAndroid(target, method, params, callID); } }
1.4 拿到对应的类名,方法名,参数后通过发射调用对应的jsapi
/** * 由JS发起的对android端的请求 * * @param className 类名 * @param methodName 方法名 * @param params 参数 * @param callID 请求ID */ public void requestAndroid(final String className, final String methodName, final String params, final long callID) { this.mWebView.post(new Runnable() { @Override public void run() { try { //拼接全类名: 包名.jsapi.className String fullClassName = mWebView.getContext().getPackageName() + ".jsapi" + "." + className; Class<?> cls = Class.forName(fullClassName); //JSAPI 方法形参为(JSBridge jsbridge,long callId,JSONObject params) Method declaredMethod = cls.getDeclaredMethod(methodName, JSBridge.class, Long.class, JSONObject.class); Object instance = cls.newInstance(); //将请求参数转换成JSONObject JSONObject requestParams; try { requestParams = new JSONObject(params); } catch (JSONException e) { requestParams = new JSONObject(); } //反射调用JSAPI declaredMethod.invoke(instance, JSBridge.this, callID, requestParams); } catch (Exception e) { reportError(callID); } } }); LogUtils.d(TAG, "requestAndroid : " + className + " , " + methodName + " , " + params); }
1.5 jsapi demo
public class JSUIControl { public void showToast(JSBridge jsBridge, Long callId, JSONObject requestParams) { String content = requestParams.optString("content"); Toast.makeText(jsBridge.getActivity(), content, Toast.LENGTH_LONG).show(); //回调JS jsBridge.reportSuccess(callId); }}
1.6 jsapi处理完逻辑后,将结果回调给js
/** * 回调JS * * @param callID 请求ID (由JS请求android端时带过来的请求ID) * @param type JSAPI执行成功与否 * @param params 回传参数 */ private void callbackJS(long callID, JSCallbackType type, String params) { try { if (callID < 0) { return; } //组装回调js StringBuilder js = new StringBuilder("javascript:"); js.append(MY_JS_BRIDGE); js.append(".callbackFromNative("); js.append(callID); js.append(","); js.append(type.getValue()); if (TextUtils.isEmpty(params)) { js.append(",{});"); } else { js.append(","); js.append(params); js.append(");"); } String callbackJS = js.toString(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //4.4及以上使用evaluateJavascript this.mWebView.evaluateJavascript(callbackJS, null); } else { this.mWebView.loadUrl(callbackJS); } LogUtils.d(TAG, "callbackJS : " + callbackJS); } catch (Exception e) { //ignore } }
1.7 js端使用alert方式调用android接口:
var json = JSON.stringify({"content":"js call native!"});alert("myjsbridge:///request?class=JSUIControl&method=showToast¶ms="+ encodeURIComponent(json)+"&callId=1");
使用:
//1.实例化JSBridge,配置WebViewJSBridge jsBridge = new JSBridge(this, webview);jsBridge.configWebView();//2.WebView 加载网页资源webview.loadUrl("file:///android_asset/demo.html");
然后结合业务,自定义jsapi
完整项目:https://github.com/snailycy/android_jsbridge
更多相关文章
- android 安卓创建文件夹
- 三步搞定:Vue.js调用Android原生方法
- 调用Android摄像头与打开相册
- android SubscriptionInfo更新流程
- [转]Android事件处理
- ch026 Android(安卓)Socket
- Android调用WebService系列之KSoap2对象解析
- Android(安卓)测试工具集02
- 安卓调用键盘回车键做保存或调用搜索键执行操作