前言:
為了加快開發效率,目前公司一些功能使用H5開發,這里難免會用到Js與Java函數互相調用的問題,這個Android是提供了原生支持的,不過存在安全隱患,今天我們來學習一種安全方式來滿足Js與java互相調用的需求。它就是WebViewJavascriptBridge。
學習動機:
先看下之前的解決辦法:Android混合開發之WebView與Javascript交互
最近棒棒安全的一個市場推廣來我們公司推廣他們的產品,當時也沒太引起我的注意,后來這個市場推廣人員把我們的app的進行了他們的安全驗證,然后發給我一份檢測報告,關於WebView的檢測內容大致如下:

其實目前公司采用H5的業務都是相對不是很重要的一些業務,而且安全性要求相對比較低,不過作為技術負責人的我,覺得現在很有必要盡快尋找一個相對安全的方式來解決這個問題,算是未雨綢繆吧。經過搜過資料尋找的解決辦法就是使用WebViewJavascriptBridge來實現Js與Java的互相調用。
WebViewJavascriptBridge介紹:
WebViewJavascriptBridge是WebView和Js交互通信的橋梁,用作者的話來說就是實現java和js的互相調用的橋梁。替代了WebView的自帶的JavascriptInterface的接口,使得開發者更方便的讓js和native靈活交互,使我們的開發更加靈活和安全。
目前實現JSBridge的開源框架很多,這里采用的hi大頭鬼hi寫的開源框架:https://github.com/lzyzsd/JsBridge
WebViewJavascriptBridge使用方式:
1.)添加配置信息
project的build.gradle中添加如下配置
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
在module的build.pradle中添加如下配置
dependencies {
compile 'com.github.lzyzsd:jsbridge:1.0.4'
}
2.)用BridgeWebView替換WebView
<com.github.lzyzsd.jsbridge.BridgeWebView android:id="@+id/test_bridge_webView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/>
3.)Js調用Java方法並傳遞數據
可以通過registerHandler()用來注冊一個java函數,來實現js回調的handler
//必須和js同名函數,注冊具體執行函數,類似java實現類。 //第一參數是訂閱的java本地函數名字 第二個參數是回調Handler , 參數返回js請求的resqustData,function.onCallBack()回調到js,調用function(responseData) mBridgeWebView.registerHandler("submitFromWeb", new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { Log.e(TAG, "指定Handler接收來自web的數據:" + data); function.onCallBack("指定Handler收到Web發來的數據,回傳數據給你"); } });
Js調用指定函數並傳遞參數
function testClick1() { //調用本地java方法 //第一個參數是 調用java的函數名字 第二個參數是要傳遞的數據 第三個參數js在被回調后具體執行方法,responseData為java層回傳數據 var data='發送消息給java代碼指定接收'; window.WebViewJavascriptBridge.callHandler( 'submitFromWeb' ,data , function(responseData) { bridgeLog('來自Java的回傳數據: ' + responseData); } ); }
也可以mBridgeWebView.setDefaultHandler()設置DefaultHandler,這樣可以接收Js通過window.WebViewJavascriptBridge通過send的所有數據
mBridgeWebView.setDefaultHandler(new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { Log.e(TAG, "DefaultHandler接收全部來自web的數據:"+data); function.onCallBack("DefaultHandler收到Web發來的數據,回傳數據給你"); } });
js實現向java發送數據
function testClick() { //發送消息給java代碼 var data = '發送消息給java代碼全局接收'; window.WebViewJavascriptBridge.send( data , function(responseData) { bridgeLog('來自Java的回傳數據: ' +responseData); } ); }
4.)Java調用Js方法並傳遞參數
//注冊事件監聽 function connectWebViewJavascriptBridge(callback) { if (window.WebViewJavascriptBridge) { callback(WebViewJavascriptBridge) } else { document.addEventListener( 'WebViewJavascriptBridgeReady' , function() { callback(WebViewJavascriptBridge) }, false ); } }
在使用WebViewJavaScriptBridge的時候需要首先判斷一下WebViewJavaScriptBridge是否存在,如果不存在需要通過添加監聽'WebViewJavascriptBridgeReady'來監聽
//注冊回調函數,第一次連接時調用 初始化函數 connectWebViewJavascriptBridge(function(bridge) { bridge.init(function(message, responseCallback) { bridgeLog('默認接收收到來自Java數據: ' + message); var responseData = '默認接收收到來自Java的數據,回傳數據給你'; responseCallback(responseData); }); bridge.registerHandler("functionInJs", function(data, responseCallback) { bridgeLog('指定接收收到來自Java數據: ' + data); var responseData = '指定接收收到來自Java的數據,回傳數據給你'; responseCallback(responseData); }); })
通過上面的鏈接WebViewJavascriptBridge可以得到一個可用WebViewJavascriptBridge,可以通過init方法來設置一個默認接收所以java發來的數據的回調,也可以通過registerHandler設置指定接收方法。
java發送數據給Js默認接收
mBridgeWebView.send("發送數據給web默認接收",new CallBackFunction(){
@Override
public void onCallBack(String data) {
Log.e(TAG, "來自web的回傳數據:" + data);
}
});
java發送數據給Js指定方法接收
mBridgeWebView.callHandler("functionInJs","發送數據給web指定接收",new CallBackFunction(){
@Override
public void onCallBack(String data) {
Log.e(TAG, "來自web的回傳數據:" + data);
}
});
5.)整個示例
為了方便學習,貼出整個示例
MainActivity
public class MainActivity extends AppCompatActivity { private static final String TAG=MainActivity.class.getSimpleName(); private BridgeWebView mBridgeWebView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); } private void initViews(){ mBridgeWebView= (BridgeWebView) findViewById( R.id.test_bridge_webView); mBridgeWebView.loadUrl("file:///android_asset/wx.html"); mBridgeWebView.setDefaultHandler(new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { Log.e(TAG, "DefaultHandler接收全部來自web的數據:"+data); function.onCallBack("DefaultHandler收到Web發來的數據,回傳數據給你"); } }); //必須和js同名函數,注冊具體執行函數,類似java實現類。 //第一參數是訂閱的java本地函數名字 第二個參數是回調Handler , 參數返回js請求的resqustData,function.onCallBack()回調到js,調用function(responseData) mBridgeWebView.registerHandler("submitFromWeb", new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { Log.e(TAG, "指定Handler接收來自web的數據:" + data); function.onCallBack("指定Handler收到Web發來的數據,回傳數據給你"); } }); findViewById(R.id.to_web_default).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mBridgeWebView.send("發送數據給web默認接收",new CallBackFunction(){ @Override public void onCallBack(String data) { Log.e(TAG, "來自web的回傳數據:" + data); } }); } }); findViewById(R.id.to_web).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mBridgeWebView.callHandler("functionInJs","發送數據給web指定接收",new CallBackFunction(){ @Override public void onCallBack(String data) { Log.e(TAG, "來自web的回傳數據:" + data); } }); } }); } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" tools:context="com.whoislcj.jsbridge.MainActivity"> <com.github.lzyzsd.jsbridge.BridgeWebView android:id="@+id/test_bridge_webView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <Button android:id="@+id/to_web_default" android:layout_margin="10dp" android:layout_width="match_parent" android:text="默認傳遞數據給Web" android:layout_height="wrap_content"/> <Button android:id="@+id/to_web" android:layout_margin="10dp" android:layout_width="match_parent" android:text="指定傳遞數據給Web" android:layout_height="wrap_content"/> </LinearLayout>
wx.html
<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <script > function testClick() { //發送消息給java代碼 var data = '發送消息給java代碼全局接收'; //第一個參數要發送的數據 第二個參數js在被回調后具體執行方法,responseData為java層回傳數據 window.WebViewJavascriptBridge.send( data , function(responseData) { bridgeLog('來自Java的回傳數據: ' +responseData); } ); } function testClick1() { //調用本地java方法 //第一個參數是 調用java的函數名字 第二個參數是要傳遞的數據 第三個參數js在被回調后具體執行方法,responseData為java層回傳數據 var data='發送消息給java代碼指定接收'; window.WebViewJavascriptBridge.callHandler( 'submitFromWeb' ,data , function(responseData) { bridgeLog('來自Java的回傳數據: ' + responseData); } ); } function bridgeLog(logContent) { document.getElementById("log_msg").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) { bridgeLog('默認接收收到來自Java數據: ' + message); var responseData = '默認接收收到來自Java的數據,回傳數據給你'; responseCallback(responseData); }); bridge.registerHandler("functionInJs", function(data, responseCallback) { bridgeLog('指定接收收到來自Java數據: ' + data); var responseData = '指定接收收到來自Java的數據,回傳數據給你'; responseCallback(responseData); }); }) </script> </head> <body> <p>WebViewJsBridge</p> <div> <button onClick="testClick()">發送數據給默認Handler接收</button> </div> <br/> <div> <button onClick="testClick1()">發送數據給指定Handler接收</button> </div> <br/> <div id="log_msg">調用打印信息</div> </body> </html>
總結:
這里僅僅是先找到了一種安全的調用方式,並沒有進行真正的商用驗證,接下來會對這個框架進一步了解,然后推廣使用。
