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

      此文出自:http://blog.csdn.net/sk719887916/article/details/47189607,skayjavascript

    按安卓開發眼下現狀來講,開發人員大部分時間仍是花在UI的屏幕適配上,使用原生控件開發成本已不是那麼理想,鑑於很是多項目和ios基於一致的ui界面。至使安卓UI開發成本花費更大的代價,所以眼下結合Html5和原生控件是解決UI適配的一種很是好的選擇。處於APP性能也會用java和native層進行結合。不管是哪一種結合。事實上原理都幾乎相同,僅僅要依照它的協議來,是很是easy的,今天咱們僅對於Html和Java層結合。學習下WebViewJavascriptBridge。html


    已經熟悉了jsbridge的朋友直接移步:

《Android基於JsBridge封裝的高效帶載入進度的WebView》:http://blog.csdn.net/sk719887916/article/details/52402470

一 什麼是webViewjavascripBridge?

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


 二 爲何要用webViewjavascripBridge?

     
    對於安卓開發有一段時間的人來講,知道安卓4.4曾經谷歌的webview存在安全漏洞,站點可以經過js注入就可以隨便拿到client的重要信息。甚至垂手可得的調用本地代碼進行流氓行爲,谷歌後來發現有此漏洞後,增長了防護措施,假設要是js調用本地代碼,開發人員 必須在代碼申明 JavascriptInterface,
   列如在4.0以前咱們要使得webView載入js僅僅需例如如下代碼:
  
mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");  

     4.4以後調用需要在調用方法增長增長@JavascriptInterface註解,假設代碼無此申明,那麼也就沒法使得js生效,也就是說這樣就可以避免惡意網頁利用js對安卓client的竊取和攻擊。android

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

     



三 怎麼使用webViewjavascripBridge


   1 將jsBridge.jar引入到咱們的project

          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>git

  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");
		//載入server網頁
		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); } }); } } } github

2 自定webVIewClient

class MyWebViewClient extends BridgeWebViewClient{

        public MyWebViewClient(BridgeWebView webView) {
            super(webView);
        }
    }

   將本身定義webViewClient設置到webview上
    
     mWebView.setWebViewClient(new MyWebViewClient(mWebView));

   上面句話很是關鍵:

      
      經過實例化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裏面的方法名一致。web

      
 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(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層(密碼和username)。同一時候在此用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的內部實現

jsbridage的封裝請參考: <Android基於JsBridge封裝的高效帶載入進度的WebView>



項目實例:https://github.com/Tamicer/JsBridge_Android
不少其它文章請關注本人微信公衆號:

相關文章
相關標籤/搜索