Android 利用WebViewJavascriptBridge 實現js和java的交互

此文出自:http://blog.csdn.net/sk719887916/article/details/47189607,skay 博客javascript

    按安卓開發目前現狀來講,開發者大部分時間仍是花在UI的屏幕適配上,使用原生控件開發成本已不是那麼理想,鑑於不少項目和iOS基於一致的ui界面,至使安卓UI開發成本花費更大的代價,所以目前結合HTML5和原生控件是解決UI適配的一種很好的選擇,處於APP性能也會用Java和native層進行結合。不論是哪一種結合,其實原理都差很少,只要按照它的協議來,是很容易的,今天咱們僅對於Html和Java層結合,學習下WebViewJavascriptBridge。html

   

一 什麼是webViewjavascripBridge?

       WebViewJavascriptBridge是移動UIView和Html交互通訊的橋樑,用做者的話來講就是實現java(ios爲oc)和js的互相調用的橋樑。替代了WebView的自帶的JavascriptInterface的接口,使得咱們的開發更加靈活和安全。html5


 二 爲何要用webViewjavascripBridge?

     
    對於安卓開發有一段時間的人來講,知道安卓4.4之前谷歌的webview存在安全漏洞,網站能夠經過js注入就能夠隨便拿到客戶端的重要信息,甚至垂手可得的調用本地代碼進行流氓行爲,谷歌後來發現有此漏洞後,增長了防護措施,若是要是js調用本地代碼,開發者 必須在代碼申明 JavascriptInterface,
   列如在4.0以前咱們要使得webView加載js只需以下代碼:
  
[java]  view plain  copy
 print ? 在CODE上查看代碼片 派生到個人代碼片
  1. mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");    

     4.4以後調用須要在調用方法加入加入@JavascriptInterface註解,若是代碼無此申明,那麼也就沒法使得js生效,也就是說這樣就能夠避免惡意網頁利用js對安卓客戶端的竊取和攻擊。java

        可是即便這樣,咱們不少時候須要在js記載本地代碼的時候,要作一些判斷和限制,或者有可能也會作些過濾和對用戶友好提示,所以JavascriptInterface也就沒法知足咱們的需求了,特此有大神就寫出了WebViewJavascriptBridge框架。android

     



三 怎麼使用webViewjavascripBridge


   1 將jsBridge.jar引入到咱們的工程

           Eclispe
         
           導入jar包, 或者直接copyjar源碼, jar能夠到gitHub上下載。

         Android Studio: 
             
repositories {
    // ...
    maven { url "https://jitpack.io" }
}

dependencies {
    compile 'com.github.lzyzsd:jsbridge:1.0.4'
}

     2 WebView包需使用以上包的webView


              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>

  3 Java代碼

      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);  
        }  
} 


   將自定義webViewClient設置到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();  
  
           }  
       });  


  3 Html和js實現

         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>  



  js代碼


    
<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註冊事件相同,ios


     當點擊「發消息給Native」的按鈕時,Js經過webWiew的jsBridage.send()發送一條數據給java層(密碼和用戶名),同時在此用function()來執行應java層回調函數的。此demo中是把java返回的數據插入到"show"的div裏面去。git

   

testClick1():此方法中調用callHandler來調用Java代碼的submitFromweb同名函數,能夠結合上面的Activty的代碼理解下,此函數調用咱們已在java註冊實現好的
github

   

//必須和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中註冊方法來註冊呢,web

    在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
相關文章
相關標籤/搜索