android与js交互-jsbridge

完整项目: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,配置WebView
JSBridge jsBridge = new JSBridge(this, webview);
jsBridge.configWebView();

//2.WebView 加载网页资源
webview.loadUrl("file:///android_asset/demo.html");

然后结合业务,自定义jsapi

完整项目:https://github.com/snailycy/android_jsbridge

你可能感兴趣的:(android,Android开发,webview,jsbridge,android)