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點選開啟連結