1. 程式人生 > >Android與前端互動之JSBridge

Android與前端互動之JSBridge

    在app中,經常會遇到一些活動推廣的頁面,大多數活動具備時效性強、運營時間短的特徵,這些活動一般都是通過H5頁面快速投放到產品的活動模組,來和使用者進行互動。如何建立web頁面和本地Native頁面的深度互動,這就接下來要重點介紹的JSBridge,先看一個效果圖:圖一JS呼叫Android,圖二Android呼叫JS。



Android呼叫JS

    在android為我們提供了WebView,他有三個方法loadUrl,LoadData,LoadDataWithBase
    (1)loadUrl:這個url可以是一個遠端的網路路徑,也可以是一個本地的uri地址。
    (2)LoadData:是LoadUri的增強版,可以指定編碼格式,不至於造成亂碼
    (3)LoadDataWithBase:可以指定資源路徑,例如一個網頁可能包含image,css,js資料夾,這時候如果使用loadUrl會導致圖片資源無法載入,佈局錯亂,這時候需要使用這個方法指定資源路徑。

    通過以上三種方式android就可以和web進行通訊了,同時android也為我們提供了addJavascriptInterface,該方法負責把Object 物件暴露成 JavaScript中的name物件。WebView存在一個漏洞,該漏洞已經在Android 4.2上修復了,即使用@JavascriptInterface代替addJavascriptInterface。另外一個問題是在Android4.4版本之前WebView使用的是Webkit,在之後的版本中採用Chromium瀏覽器核心標準,由於以上安全性和相容性問題,基本上不會使用系統原生的一些方法,這裡可以使用JSBridge來進行web端和android端的資料通訊。

JS呼叫Android

(1)webView.addJavascriptInterface()
(2)WebViewClient.shouldOverrideUrlLoading()
(3)WebChromeClient.onJsAlert()/onJsConfirm()/onJsPrompt() 方法分別回撥攔截JS對話方塊alert()、confirm()、prompt()訊息。

JSBridge與Android通訊原理

    JSBridge是一座用JavaScript搭建起來的橋,替代了WebView的自帶的JavascriptInterface的介面,使得我們的開發更加靈活和安全。一端是web,一端是native,他可以根據web和native約定好的規則來通知native要做什麼,從而實現Android和Javascript之間的互動。
    
    在Android呼叫JS我們可以使用WebView.loadUrl(“javascript:function()”)進行載入,但是當H5呼叫Android時卻不是那麼方便了。先來看看JSBridge與Android之間的通訊原理:
    
    在WebView中,有一個setWebChromeClient方法,可設定WebChromeClient物件,而這個物件中有三個方法,分別是onJsAlert,onJsConfirm,onJsPrompt,當js呼叫window物件的對應的方法,即window.alert,window.confirm,window.prompt,WebChromeClient物件中的三個方法對應的就會被觸發,這時候WebView的shouldOverrideUrlLoading根據傳輸協議就會攔截到訊息,就可以在這些方法裡面進行Android中方法的呼叫。基於此原理,JSBridge制定了一個通訊協議,類似於http協議中url傳輸協議,來看看統一資源識別符號URI組:http://host:port/path?param=value,在JSBridge中的也是類似於這種協議,如下:

    jsbridge://className:port/methodName?jsonObj

(1)className:Android端實現暴露給前端的類名;

(2)port:Android返回結果給前端的埠;

(3)methodName:前端需要呼叫的函式 ;

    網頁端給Android傳遞的引數,這裡傳遞的是一個json物件,當H5頁面呼叫進行操作時,通過JSBridge出發一個uri  scheme,通過scheme傳遞一些引數資料,Android捕獲到這些訊息後會根據scheme中的資訊呼叫相應的方法,執行完畢後呼叫JSBridge物件回撥方法,並且傳入結果和id,最後H5再回調此結果,得到反饋。如下圖: 


可以總結為以下三點:

1、Android呼叫通過loadUrl(url)呼叫JS物件,可以在URL內傳遞引數。
2、JS呼叫Android是通過shouldOverrideUrlLoading攔截uri。
3、JsBridge將資料封裝成Message,然後放進Queue,再將Queue通過協議進行傳輸。

Android端使用

1.引入庫檔案

在repositories 下引入:maven { url "https://jitpack.io" }

在dependencies引入: compile 'com.github.lzyzsd:jsbridge:1.0.4‘

2.初始化設定

        final BridgeWebView bridgeWebView = (BridgeWebView) findViewById(R.id.JsBridgeWebView);
        bridgeWebView.setDefaultHandler(new DefaultHandler());
        bridgeWebView.setWebChromeClient(new WebChromeClient());
        bridgeWebView.loadUrl("file:///android_asset/a.html");

3.註冊回撥

        /**
         * js呼叫Android
         *
         *  引數一:getUserInfo就是註冊供JS呼叫的方法名,
         *  引數二:data是JS傳過來的引數,
         *  引數三:CallBackFunction 函式中需要把JS需要的response返回給JS
         */
        bridgeWebView.registerHandler("submitFromWeb", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                Log.e("TAG", "js返回:" + data);
                //顯示js傳遞給Android的訊息
                Toast.makeText(MainActivity.this, "js返回:" + data, Toast.LENGTH_LONG).show();
                //Android返回給JS的訊息
                function.onCallBack("我是js呼叫Android返回資料:" + etText.getText().toString());
            }
        });

 如上圖一演示情況,列印日誌如下:


        /**
         * Android呼叫js
         *
         * 引數一:js中的方法名稱
         * 引數二:Android傳遞給js資料
         * 引數三:回撥介面,data為Android呼叫js方法的返回資料
         */
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                bridgeWebView.callHandler("functionInJs", "Android呼叫js的方法", new CallBackFunction() {
                    @Override
                    public void onCallBack(String data) {
                        Log.e("TAG", "onCallBack:" + data);
                        Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show();
                    }
                });
            }
        });

 如上圖二演示情況,列印日誌如下:


注意:以上回呼叫法的傳遞名submitFromWeb、functionInJs必須與js保持一致。

網頁端使用

以下為web端使用示例,備註標明的很詳細:

<html>
<head>
    <meta content="text/html; charset=utf-8" http-equiv="content-type">
    <title>js呼叫java</title>
</head>
<body>
<p>
    <input type="text" id="text1" value="請輸入測試資料" width="400px" height="200px"/>
</p>
<p>
    <input type="button" id="enter" value="呼叫安卓的方法" onclick="testClick();"
    />
</p>


<script>

         //js呼叫Android方法:接收Android傳遞過來的資料,並做處理

         function testClick() {

          //引數一:呼叫java中的方法   submitFromWeb是方法名,必須和Android中註冊時候的方法名稱保持一致
          //引數二:返回給Android端的資料,可以為字串,json等資訊
          //引數三:js接收到Android傳遞過來的資料之後的相應處理邏輯

            window.WebViewJavascriptBridge.callHandler(
               'submitFromWeb'
               , {'param': "JS成功接收到資料---"}
               , function(responseData) {
                    alert(responseData)
               }
           );
       }

       //JS註冊事件監聽
       function connectWebViewJavascriptBridge(callback) {
           if (window.WebViewJavascriptBridge) {
               callback(WebViewJavascriptBridge)
           } else {
               document.addEventListener(
                   'WebViewJavascriptBridgeReady'
                   , function() {
                       callback(WebViewJavascriptBridge)
                   },
                   false
               );
           }
       }

        //註冊回撥函式,第一次連線時呼叫 初始化函式
       connectWebViewJavascriptBridge(function(bridge) {
            //初始化
           bridge.init(function(message, responseCallback) {
               var data = {
                   'Javascript Responds': 'Wee!'
               };
               responseCallback(data);
           });


           //Android呼叫js方法:functionInJs方法名稱需要保持一致 ,並返回給Android通知

           bridge.registerHandler("functionInJs", function(data, responseCallback) {
                alert(data);
               var data = document.getElementById("text1").value;
               var responseData = "我是Android呼叫js方法返回的資料---"+ data;
               responseCallback(responseData);
           });
       })

</script>

</body>
</html>

 Android端、網頁端程式碼如上,都有詳細的備註,理解起來應該不難,最後附上原始碼,如對你有幫助送上一顆星吧:

https://github.com/yoonerloop/AndroidJSBridge點選開啟連結吐舌頭吐舌頭吐舌頭