Android 中利用WebViewJavascriptBridge 實現js和java的互動(一)

來源:互聯網
上載者:User

標籤:js和java的通訊   安卓中js和java的互動   webviewjavascripbrid   

      此文出自:http://blog.csdn.net/sk719887916/article/details/47189607 ,skay 部落格

    按安卓開發目前現狀來說,開發人員大部分時間還是花在UI的螢幕適配上,使用原生控制項開發成本已不是那麼理想,鑒於很多項目和ios基於一致的ui介面,至使安卓UI開發成本花費更大的代價,因此目前結合Html5和原生控制項是解決UI適配的一種很好的選擇,處於APP效能也會用java和native層進行結合。不管是哪種結合,其實原理都差不多,只要按照它的協議來,是很容易的,今天我們僅對於Html和Java層結合,學習下一個新的開源項目--WebViewJavascriptBridge。

   一 什麼是webViewjavascripBridge?

       WebViewJavascriptBridge是移動UIView和Html互動通訊的橋樑,用作者的話來說就是實現java(ios為oc)和js的互相調用的橋樑。替代了WebView的內建的JavascriptInterface的介面,使得我們的開發更加靈活和安全。


 二 為什麼要用webViewjavascripBridge?         對於安卓開發有一段時間的人來說,知道安卓4.4以前Google的webview存在安全性漏洞,網站可以通過js注入就可以隨便拿到用戶端的重要訊息,甚至輕而易舉調用本地代碼進行流氓行為,Google後來發現有此漏洞後,增加了防禦措施,如果要是js調用本地代碼,開發人員必須在代碼申明JavascriptInterface,   列如在4.0之前我們要使得webView載入js只需如下代碼:  
mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");  

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

        但是即使這樣,我們很多時候需要在js記載本地代碼的時候,要做一些判斷和限制,或者有可能也會做些過濾和對方便使用提示,因此JavascriptInterface也就無法滿足我們的需求了,特此有大神就寫出了WebViewJavascriptBridge架構。

     

三 怎麼使用webViewjavascripBridge      1 將jsBridge.jar引入到我們的工程         此jar,或者jar源碼我們可以到gitHub上下載。
      2 WebView包需使用以上包的webView
              Layout中使用第三方view,              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>
  3 Java代碼      
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;    }@Overrideprotected 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() {@Overridepublic 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);}@Overrideprotected 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;}}@Overridepublic void onClick(View v) {if (button.equals(v)) {            webView.callHandler("functionInJs", "data from Java", new CallBackFunction() {@Overridepublic void onCallBack(String data) {// TODO Auto-generated method stubLog.i(TAG, "reponse data from js " + data);}});}}}

            通過執行個體化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() {@Overridepublic 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註冊事件相同,


     當點擊“發訊息給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() {@Overridepublic 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);            });        })


四 總結     通過以上的程式碼範例,不難發現此架構的優雅和簡便,對js和java雙方來說,如果html中的js需要調用java代碼,而java代碼沒做任何實現,那麼這個js也是無效的,反之java代碼註冊的回呼函數,沒在js裡去實現,那麼Java也無法擷取js遠端資料,由此可見此,此通訊是雙方的,必須由雙發來約定,這樣就避免了安卓之前存在js注入漏洞,也很大的提高了安全性,也可以保證我們的網頁資料不被第三方的APP擷取,怎麼說呢,比如你的項目某一html介面,被安卓瀏覽器或者其他第三方App惡意載入你們的網站或網頁,那麼它的java代碼調用你的js函數,比如由你的Js事先註冊或者實現,不然跟本無法調用。這樣就保證了這個html的唯一性,第三方可以載入預覽你的網頁,但是第三方java不能和你的js互動通訊。同樣載入第三方的網頁時候,我們可以對第三方網頁進行一些行為的過濾,進而對使用者進行友好提示。      以上代碼只是此開源架構一部分功能,還有很多的功能需要我們去挖掘,以後再給大家解剖下此開源項目的內部實現。歡迎閱讀

項目開源地址:https://github.com/lzyzsd/JsBridge
     

著作權聲明:本文為博主原創文章,請尊重原創,轉載請標明地址。

Android 中利用WebViewJavascriptBridge 實現js和java的互動(一)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.