Android通过WebView实现Java同JavaScript之间交互

1.前言

现如今各大电商,新闻媒体等app开发中少不了原生和H5混合开发。而这些在Android 端均是通过WebView实现Java和JavaScript的交互的。就拿最近遇到的现象来举一个栗子:


Android通过WebView实现Java同JavaScript之间交互_第1张图片
百度20190816112443.png

这是一个浏览器应用,当点击红色区域的时候如果此时百度APP在后台,这时候会调起百度APP,若不在后台则跳转到下载引导页面。最近百度APP日活2亿,这个功能应该拉起不少。分析这段点击跳转的流程可以看出如下数据流
Android通过WebView实现Java同JavaScript之间交互_第2张图片
百度url.png
可以看出url字段是一段关于百度APP相关的数据,这就可以说明为何可以跳转过去了。这个如何跳转后续会讨论到。

2. 交互方式概要:

两者之间的交互方式大概可以概括为以下两种方式

  1. Java去调用JS的代码
  2. JS去调用Java的代码


    Android通过WebView实现Java同JavaScript之间交互_第3张图片
    交互方式.png
对于Android调用JS代码的方法有2种:
  1. 通过WebView的loadUrl()
  2. 通过WebView的evaluateJavascript()
对于JS调用Android代码的方法有3种:
  1. 通过WebView的addJavascriptInterface() 进行对象映射
  2. 通过 WebViewClient 的shouldOverrideUrlLoading() 方法回调拦截 url
  3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt() 方法回调拦截JS对话框alert()、confirm()、prompt() 消息。

3.整体机构

在assets目录下创建一个本地html文件如下:




    
    DoTry
    

js 调用java 模板

//点击按钮则调用callAndroid函数





对WebView做出对应的设置:

 mWebSettings.setJavaScriptEnabled(true);
 mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);

3.Java调用JavaScript

  1. 通过WebView的loadUrl()方法调用js:
    在通过webview调用loadurl,可以传入参数:
case R.id.java_js:
                //java 执行js 可以传入参数。
                mWebView.loadUrl("javascript:callJS(1)");
                break;

callJS方法是一个javascript弹框的方法,这个弹框效果在WebChromeClient 中可以接收到到

public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.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;
            }

其实这里就已经存在java-js-java的一个调用流程。通过WebChromeClient 中的一些方法可以获取到相关方法,js可以传值到java。但是如果是普通js,不是弹框这种类型的就不好获取js的返回值了。

  1. 通过WebView的evaluateJavascript():
    该方法较loadurl()方法有一个好处就是可以获取到返回值,调用方式如下:
 case R.id.java_evaluateJavascript:
                mWebView.evaluateJavascript("javascript:callJS(1)", new ValueCallback() {
                    @Override
                    public void onReceiveValue(String s) {
                        Log.i(TAG, "s is ===" + s);
                    }
                });
                break;

可以看到log如下:
Android通过WebView实现Java同JavaScript之间交互_第4张图片
gretting.png

因此evaluateJavascript可以更方便的获取返回值。

4.JavaScript调用Java

  1. 通过 WebView的addJavascriptInterface()进行对象映射。
    定义一个与js映射关系的java类
public class AndroidtoJs extends Object{

    // 定义JS需要调用的方法
    // 被JS调用的方法必须加入@JavascriptInterface注解
    @JavascriptInterface
    public void nativeJavaMethod(String msg) {//msg 由js传入
        Log.i("MainActivity","JS调用了Android的nativeJavaMethod方法==="+msg);
    }
}

在上面的html的js文件中做如下调用:

//js调用Android,对象调用该方法
    function callAndroid(){
        test.nativeJavaMethod("js调用了android中的nativeJavaMethod方法");
   }

log系统如下:


Android通过WebView实现Java同JavaScript之间交互_第5张图片
js调用java.png

可以看到js通过对象映射的方法会调用到java方法。

  1. 通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url
  • Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
  • 解析该 url 的协议
  • 如果检测到是预先约定好的协议,就调用相应方法
    在html文件中定义一个js方法,实现相关请求如下:
function callAndroidByUrl(){
            /*约定的url协议为:js://webview?arg1=111&arg2=222*/
            document.location = "js://webview?arg1=111&arg2=222";
         }

在java代码中进行url解析拦截如下:

mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Uri uri = Uri.parse(url);
                // 如果url的协议 = 预先约定的 js 协议
                // 就解析往下解析参数
                if (uri.getScheme().equals("js")) {

                    // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                    // 所以拦截url,下面JS开始调用Android需要的方法
                    if (uri.getAuthority().equals("webview")) {

                        //  步骤3:
                        // 执行JS所需要调用的逻辑
                        Log.i(TAG, "拦截到相关url,去做其他事宜。");

                    }

                    return true;
                }
                return super.shouldOverrideUrlLoading(view, url);
            }
        });

拦截到url后可以在java中做其他相关操作。这也就是一开始百度app采用这种方式做的原理。

  1. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt() 方法回调拦截JS对话框alert()、confirm()、prompt() 消息。
    其实前面在loadurl的时候已经用到这种方法了,应为js的弹框等相关操作在WebChromeClient 中均可以捕获到,故此可以在接收到js相关方法后java中处理。
    js代码如下:
      function clickprompt(){
    // 调用prompt()
    var result=prompt("js://demo?arg1=111&arg2=222");
    

在java中做如下执行即可

 mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.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;
            }

            @Override
            public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {


                return super.onJsConfirm(view, url, message, result);
            }

            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                Uri uri = Uri.parse(url);
                // 如果url的协议 = 预先约定的 js 协议
                // 就解析往下解析参数
                if (uri.getScheme().equals("js")) {

                    // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                    // 所以拦截url,下面JS开始调用Android需要的方法
                    if (uri.getAuthority().equals("webview")) {

                        //  步骤3:
                        // 执行JS所需要调用的逻辑
                        Log.i(TAG, "拦截到相关url,去做其他事宜。");
                    }
                    return true;
                }
                return super.onJsPrompt(view, url, message, defaultValue, result);
            }
        });

这就是js调用java代码的实现效果。

总结

关于java和js之间相互调用,可以简单归纳为以上集几种方法,市面上会有一些比较好的开源库可以借鉴,但其原理就是上面说的这几种。对以上方法作出如下总结:

Android通过WebView实现Java同JavaScript之间交互_第6张图片
总结.png

以上是关于WebView的一些java同js交互的问题,当然java和js之间交互会存在一些其他问题,比如漏洞等相关问题,这些都是在开发过程中需要注意到的。
文中代码地址: WebViewSample

你可能感兴趣的:(Android通过WebView实现Java同JavaScript之间交互)