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

1.前言

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


百度20190816112443.png

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

2. 交互方式概要:

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

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


    交互方式.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文件如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>DoTry</title>
    <script>
// Android需要调用的方法
   function callJS(code){
      alert("Android调用了JS的callJS方法"+code);
   }
  function getGreetings(code) {
      return code;
   }
   //js调用Android,对象调用该方法
    function callAndroid(){
        test.nativeJavaMethod("js调用了android中的hello方法");
   }

    function callAndroidByUrl(){
            /*约定的url协议为:js://webview?arg1=111&arg2=222*/
            document.location = "js://webview?arg1=111&arg2=222";
         }
          function clickprompt(){
    // 调用prompt()
    var result=prompt("js://demo?arg1=111&arg2=222");
    <!--alert("demo " + result);-->
}
</script>
</head>
js 调用java 模板
<body>
//点击按钮则调用callAndroid函数
<button type="button" id="button1" onclick="callAndroid()">js 调用java</button>
<button type="button" id="button2" onclick="callAndroidByUrl()">调用callAndroidByUrl</button>
<button type="button" id="button3" onclick="clickprompt()">点击调用Android代码</button>
</body>
</html>

对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<String>() {
                    @Override
                    public void onReceiveValue(String s) {
                        Log.i(TAG, "s is ===" + s);
                    }
                });
                break;

可以看到log如下:
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系统如下:


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");
    <!--alert("demo " + result);-->

在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之间相互调用,可以简单归纳为以上集几种方法,市面上会有一些比较好的开源库可以借鉴,但其原理就是上面说的这几种。对以上方法作出如下总结:

总结.png

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容