Markdown版本筆記 | 我的GitHub首頁 | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
WebView JS交互 JSBridge 案例 原理 MD
目錄
簡介
gradle配置
Java端:注冊提供給JS端調用的接口
JS端:注冊提供給Java端調用的接口
JS 端注意事項
源碼解析
BridgeWebView
java 調用 js 中注冊的方法
調用過程
回調過程
一個疑惑
js 調用 java 中注冊的方法
一個完整的Demo
MainActivity
注冊的BridgeHandler
HTML+JS源碼
DSBridge 簡介
特性
使用
簡介
Demo
JsBridge - Android專用
WebViewJavascriptBridge - iOS專用
PS:
這個庫是專供 Android 端使用的,iOS 端可以使用 WebViewJavascriptBridge,這兩個庫對於前端JS代碼來說是兼容的!
android java and javascript bridge, inspired by wechat webview jsbridge
This project make a bridge between Java and JavaScript.
It provides safe and convenient way to call Java code from js
and call js code from java
.
JS調用Android有三種方式:
- webView.
addJavascriptInterface()
- WebViewClient.
shouldOverrideUrlLoading()
- WebChromeClient.
onJsAlert()/onJsConfirm()/onJsPrompt()
方法分別回調攔截JS對話框alert()、confirm()、prompt()消息
Android調用JS有兩種方式:
- webView.
loadUrl()
; - webView.
evaluateJavascript()
gradle配置
在project的build.gradle中引入jitpack.io
:
allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}
在module中添加依賴:
implementation 'com.github.lzyzsd:jsbridge:1.0.4'
Java端:注冊提供給JS端調用的接口
Register a Java handler
function so that js can call:
webView.registerHandler("submitFromWeb", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
//do something
function.onCallBack("submitFromWeb exe, response data from Java"); //回調
}
});
js can call this Java handler
method "submitFromWeb" through:
WebViewJavascriptBridge.callHandler(
'submitFromWeb' //方法名
, {'param': str1} //參數
, function(responseData) { //回調
document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
}
);
You can set a default handler
in Java, so that js can send message to Java without assigned handlerName
:
webView.setDefaultHandler(new DefaultHandler());
window.WebViewJavascriptBridge.send( //沒有方法名
data
, function(responseData) {
document.getElementById("show").innerHTML = "repsonseData from java, data = " + responseData
}
);
JS端:注冊提供給Java端調用的接口
Register a JavaScript handler
function so that Java can call:
WebViewJavascriptBridge.registerHandler("functionInJs", function(data, responseCallback) {
document.getElementById("show").innerHTML = ("data from Java: = " + data);
var responseData = "Javascript Says Right back aka!";
responseCallback(responseData);
});
Java can call this js handler
function "functionInJs" through:
webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
@Override
public void onCallBack(String data) {
}
});
You can also define a default handler
use init method, so that Java can send message to js without assigned handlerName
:
for example:
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);
});
when Java call:
webView.send("hello");
will print 'JS got a message hello' and 'JS responding with' in webview console.
JS 端注意事項
This lib will inject注入 a WebViewJavascriptBridge
Object to window object
. So in your js, before use WebViewJavascriptBridge, you must detect檢測 if WebViewJavascriptBridge exist. If WebViewJavascriptBridge does not exit, you can listen to WebViewJavascriptBridgeReady
event, as the blow code shows:
if (window.WebViewJavascriptBridge) {
//do your work here
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady'
, function() {
//do your work here
},
false
);
}
源碼解析
主要有三點:
- Android調用JS是通過
loadUrl(url)
,url中可以拼接要傳給JS的對象 - JS調用Android是通過
shouldOverrideUrlLoading
- JsBridge將溝通數據封裝成
Message
,然后放進Queue
,再將Queue進行傳輸
BridgeWebView
首先自定義了一個 WebView:
public class BridgeWebView extends WebView implements WebViewJavascriptBridge
繼承的是系統自帶的 WebView,鑒於很多人會使用騰訊的 X5 WebView,如果想保持使用 X5 的話,我們其實重新定義一個 WebView 並將其繼承的 WebView 改成 X5 的就行了。
里面定義了兩個集合,一個用於在調用 registerHandler
時保存Java中注冊的 方法名 和 BridgeHandler
的映射,一個用於在調用 send
時保存 Java 向 JS 發送的請求和 CallBackFunction
的映射:
Map<String, BridgeHandler> messageHandlers = new HashMap<>();//供js調用
Map<String, CallBackFunction> responseCallbacks = new HashMap<>();//調用js
然后是一些初始化操作:
getSettings().setJavaScriptEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
setWebViewClient(new BridgeWebViewClient(this));
這里定義了一個 BridgeWebViewClient
,里面並沒有什么復雜的邏輯,僅僅是重寫了 WebViewClient 的 shouldOverrideUrlLoading
方法和 onPageFinished
方法。我們先看一下 onPageFinished 方法,shouldOverrideUrlLoading 后面我們用到時再分析。
onPageFinished 主要用於在頁面加載后加載 js 文件,以及調用一些需要在頁面加載后才能調用的 js 邏輯:
public static final String toLoadJs = "WebViewJavascriptBridge.js";
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
String jsContent = assetFile2Str(view.getContext(), toLoadJs);
view.loadUrl("javascript:" + jsContent); //加載 JS 代碼
if (webView.getStartupMessage() != null) {
for (Message m : webView.getStartupMessage()) {
webView.dispatchMessage(m);//需要在頁面加載完執行的邏輯
}
webView.setStartupMessage(null);//清空這些任務
}
}
java 調用 js 中注冊的方法
調用過程
接口 WebViewJavascriptBridge
其實就是讓我們調用 js 中注冊的方法的:
public interface WebViewJavascriptBridge {
void send(String data);
void send(String data, CallBackFunction responseCallback);
}
鑒於 js 和 java 交互無非這兩種方式:webview.addJavascriptInterface
或 WebViewClient.shouldOverrideUrlLoading
,所以其實不用看源碼我們也知道,其最終肯定是通過兩者中的一種來實現的。
我們大致瞅一下其調用過程:
send(String data) -> send(data, null);
send(String data, CallBackFunction responseCallback) -> doSend(null, data, responseCallback);
兩者都會調用 doSend
方法,這個方法其實主要就是將消息相關信息封裝到 Message
中:
private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
Message m = new Message();
if (!TextUtils.isEmpty(handlerName)) m.setHandlerName(handlerName);
if (!TextUtils.isEmpty(data)) m.setData(data);
if (responseCallback != null) {
//為此次調用定義一個唯一的回調id,例如【JAVA_CB_1_541】【JAVA_CB_2_455】
String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
responseCallbacks.put(callbackStr, responseCallback);//將回調id和對應的回調方法保存起來
m.setCallbackId(callbackStr);//將回調id賦給此消息,此id是實現交互的最關鍵的信息
//之后,當具有同一id的消息通過 shouldOverrideUrlLoading 回調給我們時,我們就可以找到並執行這里定義的回調方法
}
queueMessage(m);
}
這里用到的 Message 是庫中定義的一個非常簡單的數據結構:
public class Message{
private String callbackId; //callbackId
private String responseId; //responseId
private String responseData; //responseData
private String data; //data of message
private String handlerName; //name of handler
private final static String CALLBACK_ID_STR = "callbackId";
private final static String RESPONSE_ID_STR = "responseId";
private final static String RESPONSE_DATA_STR = "responseData";
private final static String DATA_STR = "data";
private final static String HANDLER_NAME_STR = "handlerName";
}
接下來會做一個簡單的判斷,以決定是立即調用還是在頁面加載完成后調用:
private void queueMessage(Message m) {
if (startupMessage != null) startupMessage.add(m); //在 onPageFinished 后調用
else dispatchMessage(m); //立即調用(默認操作)
}
下面是最核心的部分之一了,簡單來說就是,經過一系列操作后,最終是通過 loadUrl 來實現原生調用 js 的方法的:
void dispatchMessage(Message m) {
String messageJson = m.toJson(); //以Json格式通訊
messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");//轉義特殊字符
messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");//轉義特殊字符
String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);//調用的js命令
if (Thread.currentThread() == Looper.getMainLooper().getThread()) { //必須在主線程中調用
this.loadUrl(javascriptCommand); //最核心的部分,原理就是通過 loadUrl 調用 js 方法
}
}
js命令的全部內容例如下,其中包含的內容有:唯一的callbackId、js中定義的方法名handlerName
、傳遞過去的數據data
。
javascript:WebViewJavascriptBridge._handleMessageFromNative('{\"callbackId\":\"JAVA_CB_1_541\",\"data\":\"【包青天20094】\",\"handlerName\":\"showInHtml\"}');
至此,整個調用過程結束了。
回調過程
經過上面的操作,我們已經調用了 js 端的代碼了,下面我們探究在調用時我們注冊的回調方法是怎么被調用的。
我們不需要關心 js 那端的邏輯是怎么樣的,因為這完全是前端同學需要關注的事情,我們只關注 js 調用之后是怎么和我們原生交互的。
前面我們已經說過了,js和java交互只有兩種方式:webview.addJavascriptInterface
或 WebViewClient.shouldOverrideUrlLoading
,這個庫采用的就是重寫 shouldOverrideUrlLoading
這種方式。
我們首先看一下 shouldOverrideUrlLoading
這個方法,里面主要是根據回調 url 做不同的處理:
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) {//格式為【yy://return/{function}/returncontent】
webView.handlerReturnData(url);
return true;
} else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) { //格式為【yy://】
webView.flushMessageQueue();
return true;
} else return super.shouldOverrideUrlLoading(view, url); //不做任何處理
}
上面的案例中,我們回調的 url 為yy://__QUEUE_MESSAGE__/
,所以會調用flushMessageQueue
方法,從方法名來看,其作用應該是"刷新消息隊列"的,我們先看看其基本結構:
void flushMessageQueue() {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) { //主線程的調用才處理
//JS代碼為【"javascript:WebViewJavascriptBridge._fetchQueue();"】
loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
@Override
public void onCallBack(String data) { //處理調用后的回調
//...
});
}
}
public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
this.loadUrl(jsUrl);//調用 js 代碼
responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback); //處理后的 id 為【_fetchQueue】
}
調用完 loadUrl 之后集合 responseCallbacks
中就有兩個元素了。既然又調用了一次 loadUrl,那我們就繼續追蹤shouldOverrideUrlLoading
。這一次回調的 url 為:
yy://return/_fetchQueue/[{"responseId":"JAVA_CB_1_541","responseData":"【你好,我是白乾濤,這是JS名為showInHtml的Handler返回的數據】"}]
所以就會調用handlerReturnData
這個方法,這個方法是用於將返回的數據回調給 CallBackFunction
的,邏輯非常簡單:
void handlerReturnData(String url) {
String functionName = BridgeUtil.getFunctionFromReturnUrl(url); //固定為【_fetchQueue】
CallBackFunction f = responseCallbacks.get(functionName); //找到對應的 CallBackFunction
String data = BridgeUtil.getDataFromReturnUrl(url); //拿到 js 返回的數據
if (f != null) {
f.onCallBack(data); //將返回的數據回調給 CallBackFunction
responseCallbacks.remove(functionName);//移除此 CallBackFunction
return;
}
}
解析后,這里的:
- functionName 為
_fetchQueue
- CallBackFunction 為上面我們在調用
flushMessageQueue
時定義的回調方法 - 返回的 data 為
[{"responseId":"JAVA_CB_1_541","responseData":"【你好,我是白乾濤,這是JS名為showInHtml的Handler返回的數據】"}]
,可以看到這個 data 其實是一個JSONArray
所以接下來就進入其 onCallBack
中了,這個方法里面的代碼稍多一點點,大致邏輯為:
List<Message> list = Message.toArrayList(data); //解析返回的數據
for (int i = 0; i < list.size(); i++) { //遍歷這個集合,本例中只有id為 JAVA_CB_1_541 的元素
if (!TextUtils.isEmpty(responseId) { //如果消息的responseId不為空
function.onCallBack(responseData); //【核心】,調用相應的回調方法
responseCallbacks.remove(responseId); //執行完就移除掉
}else{ //這里面的邏輯是在js主動調用原生方法時才會執行的,我們后面再分析
}
}
完成以后,所有的回調都執行了,集合 responseCallbacks 中的所有元素也都移除了。至此整個回調過程也結束了。
一個疑惑
通過上面的流程分析,我們知道,在我們首次 loadUrl 后的 shouldOverrideUrlLoading 回調中,我們首先是進入 flushMessageQueue 方法里面又 load 了一段固定的 js 代碼,然后再在 shouldOverrideUrlLoading 回調中獲取 js 返回的數據以及執行回調,為什么要這么搞呢,明明一次交互就可以了的?
js 調用 java 中注冊的方法
這里的大多數流程和上面的基本是一致的,下面我們只簡單的過一下:
- 首先肯定還是回調
shouldOverrideUrlLoading
,此時回調的url為固定的yy://__QUEUE_MESSAGE__/
- 接着調用
flushMessageQueue
,然后又調用一次 loadUrl,消息 id 同樣為_fetchQueue
- 然后會再次回調
shouldOverrideUrlLoading
,此時的url為:
yy://return/_fetchQueue/[{"handlerName":"showTips","data":{"type":"dialog","title":"我是標題","msg":""},"callbackId":"cb_3_1540110644942"}]
- 所以會進入
handlerReturnData
,接着會回調flushMessageQueue
中的onCallBack
方法,接着就是遍歷集合 - 目前集合中只有一個元素,由於
responseId
為空,所以會走和上面分析的 java 調 js 不同的分支
我們着重分析此時要走的邏輯:
CallBackFunction responseFunction = null; //臨時定義一個回調
final String callbackId = m.getCallbackId();
if (!TextUtils.isEmpty(callbackId)) { //有 callbackId 時的回調是繼續load一個js代碼
responseFunction = new CallBackFunction() {
@Override
public void onCallBack(String data) {
Message responseMsg = new Message();
responseMsg.setResponseId(callbackId); //將 callbackId作為 responseId
responseMsg.setResponseData(data);
queueMessage(responseMsg);
}
};
} else { //沒有 callbackId 時的回調是一個空實現(正常是不會走到這里的)
responseFunction = data -> { };
}
BridgeHandler handler = TextUtils.isEmpty(m.getHandlerName() ? defaultHandler : messageHandlers.get(m.getHandlerName());
if (handler != null) handler.handler(m.getData(), responseFunction); //調用我們定義的 handler
里面首先是定義了一個 CallBackFunction
回調,然后會調用我們定義的 BridgeHandler
的 handler
方法,例如下面是本案例中我寫的部分邏輯:
@Override
public void handler(String data, CallBackFunction function) {
//...顯示Toast或彈Dialog或打印Log
function.onCallBack(...); //返回數據
}
接下來就會走我們上面定義的 responseFunction 的 onCallBack 方法,接着會走 queueMessage 方法,和上面的流程一樣,我們接着會走 dispatchMessage,然后調用 loadUrl 調用 js 的方法的,此時的 js 命令為:
javascript:WebViewJavascriptBridge._handleMessageFromNative('{\"responseData\":\"{\\\"status\\\":\\\"success\\\",\\\"time\\\":0}\",\"responseId\":\"cb_2_1540112210451\"}');
至此,便完整實現了 js 調用原生方法,並在原生響應 js 的命令后回調數據給 js。
一個完整的Demo
MainActivity
public class MainActivity extends ListActivity {
private BridgeWebView mBridgeWebView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBridgeWebView = new BridgeWebView(this);
registerHandler();
mBridgeWebView.loadUrl("file:///android_asset/jsbridge.html");
getListView().addFooterView(mBridgeWebView);
String[] array = {"調用JS中的名為showInHtml的Handler",
"調用JS中的默認Handler,沒有回調",
"調用JS中的默認Handler,有回調",};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>(Arrays.asList(array))));
}
private void registerHandler() {
mBridgeWebView.setDefaultHandler(new DefaultBridgeHandler());//默認的Handle
mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_DEVICEINFO, new DeviceInfoBridgeHandler());//獲取設備信息
mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_BACKNATIVE, new BackNativeBridgeHandler(this));//結束當前Activity
mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_SHOWTIPS, new ShowTipsBridgeHandler(this));//顯示原生toast或彈窗
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
mBridgeWebView.callHandler(JBJsIDs.BRIDGE_ID_SHOWINHTML, "【包青天20094】", new SimpleCallBackFunction());
break;
case 1:
mBridgeWebView.send("【包青天20095】");
break;
case 2:
mBridgeWebView.send("【包青天20096】", new SimpleCallBackFunction());
break;
}
}
class SimpleCallBackFunction implements CallBackFunction {
@Override
public void onCallBack(String data) {
Log.i("bqt", "【JS回調】,是否在主線程:" + (Looper.myLooper() == Looper.getMainLooper()) + "\n" + data);
//也可以用Thread.currentThread() == Looper.getMainLooper().getThread()
}
}
}
注冊的BridgeHandler
public class DeviceInfoBridgeHandler implements BridgeHandler {
/**
* @param data js調用原生所傳遞的參數
* @param function js調用原生所傳遞的回調接口
*/
@Override
public void handler(String data, final CallBackFunction function) {
Log.i("bqt", "【獲取設備信息,參數】" + data);
new Thread(() -> {
SystemClock.sleep(2000);//模擬耗時操作
DeviceInfo deviceInfo = new DeviceInfo("小米6", "8.0");
JBRespBean bridgeRespBean = JBRespBean.newBuilder()
.status(JBRespBean.STATUS_SUCCESS)
.time(System.currentTimeMillis())
.data(deviceInfo)
.build();
final String response = new Gson().toJson(bridgeRespBean);
Log.i("bqt", "【給JS的響應】" + response);
new Handler(Looper.getMainLooper()).post(() -> {
function.onCallBack(response);//必須在主線程中回調給JS,因為JS中加載網頁也是UI操作,也必須在主線程中
});
}).start();
}
static class DeviceInfo {
String phoneName;
String sysVersion;
DeviceInfo(String phoneName, String sysVersion) {
this.phoneName = phoneName;
this.sysVersion = sysVersion;
}
}
}
HTML+JS源碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSBridge測試頁面</title>
</head>
<body>
<p>
<!--xmp標簽可以在頁面上輸出html代碼-->
<xmp id="show"></xmp>
</p>
<p><input type="text" id="inputMsg" value=""/></p>
<p><input type="button" id="button1" value="獲取設備信息" onclick="deviceInfo();"/></p>
<p><input type="button" id="button2" value="顯示原生dialog" onclick="showTips();"/></p>
<p><input type="button" id="button3" value="結束當前Activity" onclick="backNative();"/></p>
<p><input type="button" id="button4" value="使用默認的Handle" onclick="defaultHandler();"/></p>
<p><input type="button" id="button5" value="顯示html源碼" onclick="showHtml();"/></p>
<p>
<xmp id="htmlContent"></xmp>
</p>
</body>
<script>
//******************************************************************************************
// 點擊事件
//******************************************************************************************
function deviceInfo() {
var inputMsg = document.getElementById("inputMsg").value;
var data = {
content: inputMsg
};
window.WebViewJavascriptBridge.callHandler("deviceInfo",
data,
function(responseData) {
document.getElementById("show").innerHTML = "【返回數據】" + responseData
}
);
}
function showTips() {
var inputMsg = document.getElementById("inputMsg").value;
var data = {
type: "dialog",
title: "我是標題",
msg: inputMsg
};
window.WebViewJavascriptBridge.callHandler('showTips',
data,
function(responseData) {
document.getElementById("show").innerHTML = "【返回數據】 " + responseData
}
);
}
function backNative() {
window.WebViewJavascriptBridge.callHandler('backNative',
null,
function(responseData) {
console.log("【此時Activity關閉了,不要再操作UI了】" + responseData);
}
);
}
function defaultHandler() {
var data = {
type: "toast",
msg: "使用默認的Handle處理"
};
window.WebViewJavascriptBridge.send(data,
function(responseData) {
window.WebViewJavascriptBridge.callHandler('showTips', data, null); //顯示原生toast
}
);
}
function showHtml() {
document.getElementById("htmlContent").innerHTML = document.getElementsByTagName("html")[0].innerHTML;
}
//******************************************************************************************
// JS端注冊提供給Java端調用的接口
//******************************************************************************************
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) {
//注冊默認的Handler
bridge.init(function(data, responseCallback) {
document.getElementById("show").innerHTML = ("【原生傳過來的數據】" + data);
if (responseCallback) { //這行代碼意思是:如果有responseCallback這個方法,就執行以下邏輯
responseCallback("【你好,我是白乾濤,這是JS默認的Handler返回的數據】");
}
});
//注冊供原生調用的Handler
bridge.registerHandler("showInHtml", function(data, responseCallback) {
document.getElementById("show").innerHTML = ("【原生傳過來的數據】" + data);
if (responseCallback) {
responseCallback("【你好,我是白乾濤,這是JS名為showInHtml的Handler返回的數據】");
}
});
})
</script>
</html>
DSBridge 簡介
三端易用的現代跨平台 Javascript bridge, 通過它,你可以在Javascript和原生之間同步或異步的調用彼此的函數.
特性
跨平台
:同時支持ios/android
- 支持
同步、異步調
用 雙向調用
:js可以調用native, native可以調用js。- 三端易用:三端指ios 、android和前端
- 支持進度回調:一次調用,多次返回
- Android支持騰訊X5內核
- 支持以類的方式集中統一管理API
- 支持API命名空間
- 支持調試模式
- 支持API存在性檢測
- 支持Javascript關閉頁面事件回調
- 支持Javascript 模態/非模態對話框
DSBridge 官方是同時支持 ios/android 的,WebViewJavascriptBridge 官方說明是支持 ios/osx 的,但 並不支持android , 當然,由於 WebViewJavascriptBridge 的人氣 實在太高,也有一些人在 android 上實現了兼容的版本,如 gzsll/WebViewJavascriptBridge,但是總的來說,並非一家之作,這可能會給日后維護帶來問題。
到目前為止,據作者所知,跨平台的 js bridge 中,DSBridge 是唯一一個支持同步調用的!
使用
1、添加依賴
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
implementation 'com.github.wendux:DSBridge-Android:3.0-SNAPSHOT'
//或
implementation 'com.github.wendux:DSBridge-Android:x5-3.0-SNAPSHOT' //支持X5內核
2、原生定義供JS調用的API
public class JsApi {
@JavascriptInterface
public String testSyn(Object msg) {
return msg + "[syn call]"; //同步API
}
@JavascriptInterface
public void testAsyn(Object msg, CompletionHandler<String> handler) {
handler.complete(msg + " [ asyn call]"); //異步API
}
}
3、JS定義供原生調用的API
//cdn方式引入初始化代碼(中國地區慢,建議下載到本地工程)
//<script src="https://unpkg.com/dsbridge@3.1.3/dist/dsbridge.js" />
//npm方式安裝初始化代碼
//npm install dsbridge@3.1.3
var dsBridge=require("dsbridge")
//注冊 javascript API
dsBridge.register('addValue',function(l,r){
return l+r;
})
4、JS調用原生API
var str=dsBridge.call("testSyn","testSyn"); //同步調用
dsBridge.call("testAsyn","testAsyn", function (v) {
alert(v); //異步調用
})
5、原生調用JS API
DWebView dwebView= (DWebView) findViewById(R.id.dwebview);
dwebView.addJavascriptObject(new JsApi(), null); //Object object, String namespace
dwebView.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue<Integer>() {
@Override
public void onValue(Integer retValue) {
Log.d("jsbridge", "call succeed, return value is " + retValue);
}
});
2018-5-8