此文出自:,skay 博客

    按安卓开发目前现状来说,开发者大部分时间还是花在UI的屏幕适配上,使用原生控件开发成本已不是那么理想,鉴于很多项目和iOS基于一致的ui界面,至使安卓UI开发成本花费更大的代价,因此目前结合HTML5和原生控件是解决UI适配的一种很好的选择,处于APP性能也会用Java和native层进行结合。不管是哪种结合,其实原理都差不多,只要按照它的协议来,是很容易的,今天我们仅对于Html和Java层结合,学习下WebViewJavascriptBridge。

一 什么是webViewjavascripBridge?

WebViewJavascriptBridge是移动UIView和Html交互通信的桥梁,用作者的话来说就是实现java(ios为oc)和js的互相调用的桥梁。替代了WebView的自带的JavascriptInterface的接口,使得我们的开发更加灵活和安全。


     

必须在代码申明JavascriptInterface,

   列如在4.0之前我们要使得webView加载js只需如下代码:

[java] view plain copy


 print?

    1. mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");

    需要在调用方法加入加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这样就可以避免恶意网页利用js对安卓客户端的窃取和攻击。

    JavascriptInterface也就无法满足我们的需求了,特此有大神就写出了WebViewJavascriptBridge框架。

         



    三 怎么使用webViewjavascripBridge


              Eclispe

             

               导入jar包, 或者直接copyjar源码, jar可以到gitHub上下载。


            Android Studio: 

    repositories {
        // ...
        maven { url "https://jitpack.io" }
    }
    
    dependencies {
        compile 'com.github.lzyzsd:jsbridge:1.0.4'
    }



                  Layout中使用第三方webView

           EG:

    <?xml version="1.0" encoding="utf-8"?>  
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:orientation="vertical" >  
      
        <!-- button 演示Java调用web -->  
        <Button   
            android:id="@+id/button"  
            android:layout_width="match_parent"  
            android:text="@string/button_name"  
            android:layout_height="48dp"  
            />  
          
        <!-- webview 演示web调用Java -->  
        <com.github.lzyzsd.jsbridge.BridgeWebView  
            android:id="@+id/webView"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent" >  
         </com.github.lzyzsd.jsbridge.BridgeWebView>  
      
    </LinearLayout>  <span style="color: black; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 9px; line-height: 18px; background-color: inherit;">  </span>

          1 activity


    public class MainActivity extends Activity implements OnClickListener {  
      
        private final String TAG = "MainActivity";  
      
        BridgeWebView webView;  
      
        Button button;  
      
        int RESULT_CODE = 0;  
      
        ValueCallback<Uri> mUploadMessage;  
      
        static class Location {  
            String address;  
        }  
      
        static class User {  
            String name;  
            Location location;  
            String testStr;  
        }  
      
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
      
            webView = (BridgeWebView) findViewById(R.id.webView);  
      
            button = (Button) findViewById(R.id.button);  
      
            button.setOnClickListener(this);  
      
            webView.setDefaultHandler(new DefaultHandler());  
      
            webView.setWebChromeClient(new WebChromeClient() {  
      
                @SuppressWarnings("unused")  
                public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType, String capture) {  
                    this.openFileChooser(uploadMsg);  
                }  
      
                @SuppressWarnings("unused")  
                public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType) {  
                    this.openFileChooser(uploadMsg);  
                }  
      
                public void openFileChooser(ValueCallback<Uri> uploadMsg) {  
                    mUploadMessage = uploadMsg;  
                    pickFile();  
                }  
            });  
            //加载本地网页  
      
            //webView.loadUrl("file:///android_asset/demo.html");  
            //加载服务器网页  
            webView.loadUrl("https://www.baidu.com");  
      
            //必须和js同名函数,注册具体执行函数,类似java实现类。  
            webView.registerHandler("submitFromWeb", new BridgeHandler() {  
      
                @Override  
                public void handler(String data, CallBackFunction function) {  
      
                    String str ="这是html返回给java的数据:" + data;  
                    // 例如你可以对原始数据进行处理  
                    makeText(MainActivity.this, str, LENGTH_SHORT).show();  
      
                    Log.i(TAG, "handler = submitFromWeb, data from web = " + data);  
                    function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));  
                }  
      
            });  
            //模拟用户获取本地位置  
            User user = new User();  
            Location location = new Location();  
            location.address = "上海";  
            user.location = location;  
            user.name = "Bruce";  
      
            webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {  
                @Override  
                public void onCallBack(String data) {  
                    makeText(MainActivity.this, "网页在获取你的位置", LENGTH_SHORT).show();  
      
                }  
            });  
      
            webView.send("hello");  
      
        }  
      
        public void pickFile() {  
            Intent chooserIntent = new Intent(Intent.ACTION_GET_CONTENT);  
            chooserIntent.setType("image/*");  
            startActivityForResult(chooserIntent, RESULT_CODE);  
        }  
      
        @Override  
        protected void onActivityResult(int requestCode, int resultCode, Intent intent) {  
            if (requestCode == RESULT_CODE) {  
                if (null == mUploadMessage){  
                    return;  
                }  
                Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();  
                mUploadMessage.onReceiveValue(result);  
                mUploadMessage = null;  
            }  
        }  
      
        @Override  
        public void onClick(View v) {  
            if (button.equals(v)) {  
                webView.callHandler("functionInJs", "data from Java", new CallBackFunction() {  
      
                    @Override  
                    public void onCallBack(String data) {  
                        // TODO Auto-generated method stub  
                        Log.i(TAG, "reponse data from js " + data);  
                    }  
      
                });  
            }  
      
        }  
      
    }

    2 自定webVIewClient

    class MyWebViewClient extends BridgeWebViewClient{  
      
            public MyWebViewClient(BridgeWebView webView) {  
                super(webView);  
            }  
    }



        

    mWebView.setWebViewClient(new MyWebViewClient(mWebView));<span style="color: black; line-height: 18px; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; background-color: inherit;"> </span>


       上面句话很关键:


          

          通过实例化webView,用法和安卓原生的view没多大区别,设置WebChromClient, 设置加载的html(同样支持网络和本地文件),接着我们需要给web注册和html端约定好的js方法名。代码列举的submitFromweb和js的执行的方法名一致,玩过NDK的JNI调用的朋友也知道必须和c代码之间有个约定,其实js桥和jni有点类似,


    通过注册handler来实现回调,Java代码中通过js返回的数据,进行处理后在调用function.onCallback返回给js.这里不做过多解释


    //必须和js函数同名,注册具体执行回调函数,类似java实现类。  
            webView.registerHandler("submitFromWeb", new BridgeHandler() {  
      
                @Override  
                public void handler(String data, CallBackFunction function) {  
      
                    String str ="这是html返回给java的数据:" + data;  
                    // 例如你可以对原始数据进行处理  
                    makeText(MainActivity.this, str, LENGTH_SHORT).show();  
      
                    Log.i(TAG, "handler = submitFromWeb, data from web = " + data);  
                    function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));  
                }  
      
            });



         如果是webview刚开始就执行一段Java代码 ,可以通过webView.CallHandler()来实现 。当然我们注册的方法也要和js里面的方法名一致。

          

    webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {  
               @Override  
               public void onCallBack(String data) {  
                makeText(MainActivity.this, "网页在获取你的位置", LENGTH_SHORT).show();  
      
               }  
           });




        

    java对接交通系统 java交互_html

    <html>  
        <head>  
            <meta content="text/html; charset=utf-8" http-equiv="content-type">  
            <title>  
                js调用java  
            </title>  
        </head>  
          
        <body>  
            <p>  
                <xmp id="show">  
                </xmp>  
            </p>  
            <p>  
                <xmp id="init">  
                </xmp>  
            </p>  
            <p>  
                <input type="text" id="text1" value="用户名(username)" />  
            </p>  
            <p>  
                <input type="text" id="text2" value="password" />  
            </p>  
            <p>  
                <input type="button" id="enter" value="发消息给Native" onclick="testClick();"  
                />  
            </p>  
            <p>  
                <input type="button" id="enter1" value="调用Native方法" onclick="testClick1();"  
                />  
            </p>  
            <p>  
                <input type="button" id="enter2" value="显示html" onclick="testDiv();" />  
            </p>  
            <p>  
                <input type="file" value="打开文件"  />  
            </p>  
        </body>  
          
    </html>





    <script>  
      
           function testDiv() {  
               document.getElementById("show").innerHTML = document.getElementsByTagName("html")[0].innerHTML;  
           }  
      
           function testClick() {  
               var str1 = document.getElementById("text1").value;  
               var str2 = document.getElementById("text2").value;  
      
               //发送消息给java代码  
               var data = "name=" + str1 + ",pass=" + str2;  
      
               window.WebViewJavascriptBridge.send(  
                   data  
                   , function(responseData) {  
      
                       document.getElementById("show").innerHTML = "repsonseData from java, data = " +responseData  
                   }  
               );  
      
           }  
      
           function testClick1() {  
               var str1 = document.getElementById("text1").value;  
               var str2 = document.getElementById("text2").value;  
      
               //调用本地java方法  
               window.WebViewJavascriptBridge.callHandler(  
                   'submitFromWeb'  
                   , {'param': str1}  
                   , function(responseData) {  
                       document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData  
                   }  
               );  
           }  
      
           function bridgeLog(logContent) {  
               document.getElementById("show").innerHTML = logContent;  
           }//注册事件监听  
           function connectWebViewJavascriptBridge(callback) {  
               if (window.WebViewJavascriptBridge) {  
                   callback(WebViewJavascriptBridge)  
               } else {  
                   document.addEventListener(  
                       'WebViewJavascriptBridgeReady'  
                       , function() {  
                           callback(WebViewJavascriptBridge)  
                       },  
                       false  
                   );  
               }  
           }  
           //注册回调函数,第一次连接时调用 初始化函数  
           connectWebViewJavascriptBridge(function(bridge) {  
               bridge.init(function(message, responseCallback) {  
                   console.log('JS got a message', message);  
                   var data = {  
                       'Javascript Responds': 'Wee!'  
                   };  
                   console.log('JS responding with', data);  
                   responseCallback(data);  
               });  
      
               bridge.registerHandler("functionInJs", function(data, responseCallback) {  
                   document.getElementById("show").innerHTML = ("data from Java: = " + data);  
                   var responseData = "Javascript Says Right back aka!";  
                   responseCallback(responseData);  
               });  
           })  
       </script>


          这段代码不难理解,我们对上面的id为enter的Button注册了一个点击事件,点击后执行以下testClick()方法,依次类推,其他Button注册事件相同,


         当点击“发消息给Native”的按钮时,Js通过webWiew的jsBridage.send()发送一条数据给java层(密码和用户名),同时在此用function()来执行应java层回调函数的。此demo中是把java返回的数据插入到"show"的div里面去。

       

    testClick1():此方法中调用callHandler来调用Java代码的submitFromweb同名函数,可以结合上面的Activty的代码理解下,此函数调用我们已在java注册实现好的

       


    //必须和js同名函数,注册具体执行函数,类似java实现类。  
            webView.registerHandler("submitFromWeb", new BridgeHandler() {  
      
                @Override  
                public void handler(String data, CallBackFunction function) {  
      
                    String str ="这是html返回给java的数据:" + data;  
                    // 例如你可以对原始数据进行处理  
                    makeText(MainActivity.this, str, LENGTH_SHORT).show();  
      
                    Log.i(TAG, "handler = submitFromWeb, data from web = " + data);  
                    function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));  
                }  
      
            });




         java注册Js函数如上面列子 ,那么在js中注册方法来注册呢,

        在js中我们同样可以直接注册一个回调函数,通过 bridge.registerHandler()来注册,接着调用 responseCallback(responseData)来返回数据给java代码的同名函数。f

    也可以直接调用init()来指定网页首次加载上面注册java代码。

      


    connectWebViewJavascriptBridge(function(bridge) {  
              bridge.init(function(message, responseCallback) {  
                  console.log('JS got a message', message);  
                  var data = {  
                      'Javascript Responds': 'Wee!'  
                  };  
                  console.log('JS responding with', data);  
                  responseCallback(data);  
              });  
      
              bridge.registerHandler("functionInJs", function(data, responseCallback) {  
                  document.getElementById("show").innerHTML = ("data from Java: = " + data);  
                  var responseData = "Javascript Says Right back aka!";  
                  responseCallback(responseData);  
              });  
          })





    四 总结

      

       

    通过以上的API介绍,代码示例,不难发现此框架的优雅和简便,对js和java双方来说,如果Html中的js需要调用java代码,而java代码没做任何实现,那么js中方法也是无效的,反之java代码注册的函数,没在js里去回调实现,那么Java层也是无法获取js中数据的,由此可见,此通信是双方支持的,必须由双方来约定,这样就避免了Android之前存在的js注入漏洞,也很大的提高了安全性,也可以保证我们的网页数据不被第三方的APP获取,具体来讲,列如我们的项目某一个web的h5界面,被系统浏览器或者其他第三方App的恶意加载,那么它的java代码想调用你的js函数,实现需要你的H5的Js先注册,不然跟本无法调用你的h5信息。这样保证了这个html数据的安全性,,第三方的浏览器可以加载预览你的网页,但是第三方java无法和你的的h5中的js交互通信的。同样加载我们自己的APP加载第三方的网页时候,我们可以对第三方网页进行一些行为的过滤,方便保护我们手机的安全,列如第三方可以获取本机地址时我们可以提示用户授权。虽然H5并不属于插件的一种,但是借助h5我可以方便的去更新一些运营活动,和某些需要经常需要更换UI的业务功能的地方,以上只JSBridge的使用姿势,,以后再给大家解剖下JsBridge的内部实现。




    项目地址

    项目实例:https://github.com/NeglectedByBoss/JsBridge_Android

    英文介绍:https://github.com/lzyzsd/JsBridge