Android WebView系列(二)Android和JS互调,BridgeWebView的使用。

标签: Android和Js通信  Android和Js互相调用  JsBridge  BridgeWebView  WebView内存泄漏

前言

上篇介绍了WebView的基本使用,WebView使用中常用的类和方法。本篇将介绍WebView中Android原生Js之间交互。以及它们之间通信桥梁JsBridge

1.Android调用JS

Android调用JS有两种方法,第一种是通过loadUrl()方法,第二种是通过evaluateJavascript()

1-1.loadUrl()示例

首先新建index.html放在android工程的assets目录下。

看下Html中的JS代码和Android中调用,JS代码相对比较简单。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>WebView测试</title>
    </head>

    <script type="text/javascript">
        <!--android将调用的方法-->
        function androidCallJs(){
            alert("android 调用了 JS了")
        }
    </script>
</html>

看下Android中如何实现调用JavaScript代码的。

布局文件:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<Button
    android:id="@+id/callJsBtn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="点击调用 JS" />

Kotlin代码:

   /*初始化webview*/
   val webView = getView(R.id.webview)

   /*设置webview选项参数*/
   val webSetting = webView!!.settings

   /*设置webview可以调用javascript代码*/
   webSetting.javaScriptEnabled = true

   /*设置javascript可以自动弹弹窗*/
   webSetting.javaScriptCanOpenWindowsAutomatically = true

   /*加载html,这里android_asset是固定写法*/
   webView!!.loadUrl("file:///android_asset/index.html")

   callJsBtn = getView(R.id.callJsBtn)

   callJsBtn!!.setOnClickListener {
     /*点击调用Js方法*/
     webView!!.loadUrl("javascript:androidCallJs()")

    }

    /*webview是加载整个html作用,内容的渲染需要使用它的功能辅助类WebviewChromClient类去实现*/
    webView!!.webChromeClient = object : WebChromeClient() {

      override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {

         val b = AlertDialog.Builder([email protected]);
         b.setTitle("Android Call Js")
         b.setMessage(message)
         b.setPositiveButton("确定",object :DialogInterface.OnClickListener{
                    override fun onClick(dialog: DialogInterface?, which: Int) {
                  result!!.confirm()
              }
          })
          b.setCancelable(false)
          b.create().show()
          return true
      }

  }

Android调用JS

1-2.evaluateJavascript()示例

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>WebView测试</title>
    </head>

    <script type="text/javascript">
        <!--android调用js代码第二种方法 -->
        function androidCallJs(){
            alert("android evaluateJs 调用了 JS了")

            return "Android你能收到消息吗?"
        }

    </script>
</html>
callJsBtn!!.setOnClickListener {

    /*第二种方法调用JS*/
    webView!!.evaluateJavascript("javascript:androidCallJs()", object : ValueCallback<String> {
       /*Js返回的值*/
       override fun onReceiveValue(value: String?) {     
              ToastUtils.show(value!!)
       }
     })
      // webView!!.loadUrl("javascript:androidCallJs()")

}

这里写图片描述

只要将第一种方法loadUrl()更换为evaluateJavascript()方法即可。这种方法相比较第一种方法,能够接收到JS返回值,loadUrl()只能显示。

2.JS调用Android

2-1.WebView提供方法 addJavascriptInterface() 将Js和Android建立映射关系。

2-1-1.Html声明映射对象以及Android被调用的方法

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>JS调用Android测试1</title>
    </head>

    <script type="text/javascript">

      function JsCallAndroid(){
        <!--testHello是android中注册的对象,helloJs()是android中的方法-->
        testHello.helloJs("JsCallAndroid")

      }
    </script>

    <body>
        <button type="button" id="btn" onclick="JsCallAndroid()">点击调用Android代码</button>
    </body>
</html>

2-1-2.定义与Js映射关系的Android类

/*
 * 创建者:    Ho
 * 创建时间:  2018/6/6 on 19:46
 * 描述:      Js映射的Android类
 * 其他说明: 无
 */
class AndroidToJs : Object() {
    //注解映射
    @JavascriptInterface
    fun helloJs(msg: String) {
        ToastUtils.show("Js 调用了 Android helloJs方法了")
    }
}

2-1-3.Kotlin添加Js和Android映射关系

/*初始化webview*/
webView = getView(R.id.webview)

/*设置webview选项参数*/
val webSetting = webView!!.settings

/*设置webview可以调用javascript代码*/
webSetting.javaScriptEnabled = true

/*加载html*/
webView!!.loadUrl("file:///android_asset/index.html")

/*Webview添加Js和Android映射关系*/
webView!!.addJavascriptInterface(AndroidToJs(), "testHello")

AndroidToJs()是Android的映射类。Kotiln的写法。“testHello”是映射到JS的映射类的对象,名字可以随意取。

Js调用Android

2-2. WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截消息解析。

<!DOCTYPE html>
<html>
     <head>
         <meta charset="utf-8">
         <title>JS调用Android测试1</title>
      </head>
      <script>
         function clickprompt(){
         // 调用prompt()  定义js和android通信协议
         var result=prompt("js://test?param1=hello11&param1=hello22");
         alert("test" + result);
        }
      </script>

      <!-- 点击按钮则调用clickprompt()  -->
      <body>
         <button type="button" id="btn" onclick="clickprompt()">点击调用Android代码</button>
        </body>
    </html>
webView!!.webChromeClient = object : WebChromeClient() {

    override fun onJsPrompt(view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult?): Boolean {
         val uri = Uri.parse(message)
         // 如果uri协议 = 预先约定的 js 协议,解析参数
         if (uri.scheme == "js") {
             // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
             // 所以拦截url,下面JS开始调用Android需要的方法
             if (uri.authority == "webview") {
             // 执行JS所需要调用的逻辑
             ToastUtils.show("Js 调用了 Android 中 helloToJS代码")

             // 可以在协议上带有参数并传递到Android上
             val params = HashMap<String, String>()
             var collection = uri!!.queryParameterNames

             //参数result:代表消息框的返回值(输入值)
             result!!.confirm("js调用了Android的方法成功啦")

            }
            return true
           }
           return super.onJsPrompt(view, url, message, defaultValue, result)
     }
}

WebViewAndroid4.2以下版本产生安全问题有一种情况就是通过第一种添加Js和Android映射关系的方式。第二种方式就是较为安全,不会产生安全漏洞问题。但是要Android和Js约定一个协议通信,相对实现起来比较复杂,onJsAlert()和onJsConfirm()和上述实现方式一样。

3.BridgeWebView(Hybrid)

BridgeWebView,继承了WebView类,实现了WebViewJavascriptBridge接口。各位大佬一看就知道是一个Js和Android通信的封装类。这里介绍下它的使用方式。

3-1.Gradle引入

compile 'com.github.lzyzsd:jsbridge:1.0.4'

3-2.使用方法

3-2-1.自定义JS事件回调类

private class MyHadlerCallBack extends DefaultHandler {

   @Override
   public void handler(String data, CallBackFunction function) {
       if (function != null) {
           Toast.makeText(JsBridgeVoteActivity.this, "自定义类继承DefaultHandler:" + data, Toast.LENGTH_SHORT).show();
         }
     }
}

3-2-2.自定义JS事件回调类

/*创建实例*/
BridgeWebView webView = new BridgeWebView(this);
/*设置默认回调处理类*/
webView.setDefaultHandler(new MyHadlerCallBack());
/*注册Js和android之间通信的事件*/
webView.registerHandler("openLogin", (data, function) -> openLogin());

以上面的代码为例,在Android中注册了事件 “openLogin”,这里也需要在Html页面的Js代码中进行注册相同的事件名称 “openLogin”。意味着在Html页面上用户(未登录情况下)触发登录操作。登录事件要在boolean isBindPhone代码中完成。

private void openLogin() {
     new LoginDialog(this, new LoginDialog.OnLoginCallBack() {
         @Override
         public void login() {
             HashMap<String, Object> params = new HashMap<>();
             /*method是和js约定,这是调用方法,后面是登录成功操作*/
             params.put("method", "loginSuccess");
             /*webView通知js已经登录成功*/
             webView.callHandler("call", new Gson().toJson(params), data ->{                

             });
          }
   }).show();
}

Js调用Android登录方法,Android端登录成功后通过webView.callHandler方法通知H5页面(Js)客户端登录成功,做相应的操作即可。BridgeWebView的使用方法就是这么简单,内部已经将通信机制封装好。

结尾

本篇介绍了WebView上Js和Android互相调用方法。在大前端概念的趋势下,原生AndroidH5(JS)的联系会越来越紧密。下篇将介绍在重构过程中WebView上遇到的问题,以及WebView最让人关系的内存泄漏问题以及解决方案。对于H5和Java层结合的混合开发Hybrid的研究也将在以后的WebView系列中做介绍。

Android WebView系列(一)WebView的基本使用

原文链接:加载失败,请重新获取