android_jsbridge,让你和前端愉快的交互

2,177 阅读5分钟

android_js

前言

作为开发人员都知道,客户端的版本更新对于用户来说代价是很大的。为了满足客户端能够快速更新迭代的要求,许多app都内嵌入了H5,比如很多电商平台,淘宝、京东、
聚划算等等。这类技术的关键就是在于Android客户与Web前端之间的交互。很多大型项目的接口为了防止Spammer的侵入,都是要求只能由客户端发起请求的。所以本项目
就封装了一个module,实现客户端接收前端的调用,然后由客户端发起Http请求的功能。
开始介绍项目之前,先来快速回顾一下Android客户端与Web前端之间交互的几种方式。

1. Android调用JS方法

1.1 通过WebView的loadUrl()

android客户端代码:

 private void initWebView() {
    WebSettings webSettings = webView.getSettings();
    webSettings.setJavaScriptEnabled(true); // 设置与Js交互的权限
    webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 设置允许JS弹窗

    webView.loadUrl("file:///android_asset/javascript.html");
    webView.setWebChromeClient(new WebChromeClient() {

        @Override
        public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
            AlertDialog.Builder b = new AlertDialog.Builder(SimpleWebViewActivity.this);
            b.setTitle("Alert");
            b.setMessage(message);
            b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            });
            b.setCancelable(false);
            b.create().show();
            return true;
        }
    });
}

private void setListener() {
    btnLoadUrl.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            webView.post(new Runnable() {
                @Override
                public void run() {
                    // 此处的callJS方法名与JS中的function方法名必须要一致
                    webView.loadUrl("javascript:callJS()");
                }
            });
        }
    });
}

javascript前端代码:

  <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>Carson_Ho</title>
        // JS代码
        <script>
            // Android需要调用的方法
           function callJS() {
              alert("Android调用了JS的callJS方法");
           }

        </script>
    </head>
    </html>
运行结果如图

    android客户端代码:

 btnEvaluateJavascript.setOnClickListener(new View.OnClickListener() {

        @TargetApi(19)
        @Override
        public void onClick(final View v) {
            webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String value) {
                    //此处为 js 返回的结果
                    Log.d(TAG, "value---" + value);
                }
            });
        }
    });
运行结果跟上图是一样的。
两种交互方式的比较
调用方式 优点 缺点 使用场景
使用loadUrl() 方便简洁 效率低;获取返回值麻烦 不需要使用返回值,对性能要求较低
使用evaluateJavascript() 效率高 向下兼容性差(仅Android 4.4以上可用) Android 4.4及以上

1.3 使用建议

// Android版本变量
final int version = Build.VERSION.SDK_INT;
// 因为该方法在 Android 4.4 版本才可使用,所以使用时需进行版本判断
if (version < 18) {
    webView.loadUrl("javascript:callJS()");
} else {
    webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此处为 js 返回的结果
        }
    });
}

2. JS调用Android方法

2.1 通过WebView的addJavascriptInterface()进行对象映射

代码不再贴出,详细代码请参见:代码地址    运行结果如下:

2.2 通过 WebViewClient 的方法shouldOverrideUrlLoading()回调拦截 url

代码不再贴出,详细代码请参见:代码地址    运行结果如下:

2.3 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt()消息

代码不再贴出,详细代码请参见:代码地址    运行结果如下:

2.3.1 onJsAlert()、onJsConfirm()、onJsPrompt()三者之间的比较
方法 作用 返回值
alert() 弹出警告框 没有
confirm() 弹出确认框 两个返回值(true或false)
prompt() 弹出输入框 任意设置返回值
2.3.2 总结
常用的拦截是:拦截 JS的输入框(即prompt()方法),因为只有prompt()可以返回任意类型的值,操作最全面方便、更加灵活,
而alert()对话框没有返回值,confirm()对话框只能返回两种状态(确定 / 取消)两个值。

2.4 三种Android Call Js 方式的对比以及使用场景

调用方式 优点 缺点 使用场景
通过WebView的addJavascriptInterface()
进行对象映射
方便简洁 Android 4.2以下存在漏洞问题 Android 4.2以上相对简单的互调场景
通过 WebViewClient 的方法shouldOverrideUrlLoading()
回调拦截 url
不存在漏洞问题 有协议约束,客户端向前端传值繁琐 不需要返回值的互调场景
通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法
回调拦截JS对话框alert()、confirm()、prompt()消息
不存在漏洞问题 有协议约束 能满足大多数情况下的互调场景

附:WebView的addJavascriptInterface()方法在Android 4.2以下存在的漏洞

以上都是对基础知识的回顾,下面的才是本项目的解释说明。注意了,以下才是本项目的解释说明!!

先来看一下项目运行的效果图:

大家会说,这个不是跟拦截JS的prompt()方法一样么,没错,js和android之间的交互,也无非上面提到的几种方法,这里做的封装采用的是:
js调android:通过WebView的addJavascriptInterface()进行对象映射
android回调js:android 4.4以上采用WebView的evaluateJavascript()方法,android 4.4以下采用loadUrl()方法
android端代码:

JsBridgeWebView 这是一个继承WebView的类,它里面向JS注入了一个对象供JS调用,JS可以通过这个对象调用Native的方法,调哪个方法,传哪些参数,完全由JS决定,方法名必须两端协议,Native通过反射找到对应的方法。传递过来的参数重包含了JS回调方法的方法名,客户端执行完相应的操作之后再去执行JS的方法。

js代码:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
        <title>Carson</title>
        <script>
            function callAndroid(){
                // 由于对象映射,所以调用jsCallback对象等于调用Android映射的对象
                var json = "{\"name\": \"zlove\", \"_dscbstub\": \"callback\"}"
                _jsbridge.call("testAsync", json);
             }
             function callback(result) {
                alert("客户端返回的结果是:" + result)
             }
        </script>
</head>
<body>
<!-- 点击按钮则调用callAndroid函数 -->
<button type="button" id="button1" style="font-size:30px" onclick="callAndroid()">Call Android</button>
</body>
</html>
var json = "{\"name\": \"zlove\", \"_dscbstub\": \"callback\"}" 
_jsbridge.call("testAsync", json);
_jsbridge表示客户端注入的对象,testAsync是方法名,json是参数,而_dscbstub对应的callback是js的回调方法。

end

本来以为要写很多,事实上其实把基础原理写清楚了,也就这么多😂😂😂。
I hope this will help you!

附:源码地址