简介
java跟js交互的框架。
git地址:https://github.com/lzyzsd/JsBridge
使用
git地址:https://github.com/kbjay/kj_jsbridge_use
关于导包,可以按照jsbridge的使用说明来
1
2
3
4
5
6
7
8repositories {
// ...
maven { url "https://jitpack.io" }
}
dependencies {
compile 'com.github.lzyzsd:jsbridge:1.0.4'
}为了方便分析源码,我这边的demo(kj_kjbrigde_use)是将jsbridge作为一个module(library)引入。
java传递消息给js:两种方式一种是默认方式,一种是spec方式
java中代码如下:
webView.callHandler(tag, msg,callback):发送消息msg给js中tag对应的function,function执行完成之后有一个callback。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* java 发送消息给 js
*/
private void java2jsSpec() {
// java2js-> step1
webView.callHandler("functionInJs", "发送消息给js(spec方式!!)", new CallBackFunction() {
public void onCallBack(String data) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
}
});
}
/**
* java 发送消息给 js
*/
private void java2jsDefault() {
webView.send("发送消息给js(默认方式!)", new CallBackFunction() {
public void onCallBack(String data) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
}
});
}对应的js代码如下:
js中需要注册接受消息handler(同样有两种类型:默认跟spec),处理完消息之后调用responseCallback(callbackMsg)将处理结果回传给java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35//注册事件监听,初始化
// Java2js-> step2
function initJsBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady'
, function () {
callback(WebViewJavascriptBridge)
},
false
);
}
}
//回调函数,接收java发送来的数据
initJsBridge(function (bridge) {
//默认接收
bridge.init(function (message, responseCallback) {
document.getElementById("show").innerHTML = '默认接收到Java的数据: ' + message;
var responseData = 'js默认接收完毕,并回传数据给java';
responseCallback(responseData); //回传数据给java
});
//指定接收,参数functionInJs 与java保持一致
//java2js -> step 3;
bridge.registerHandler("functionInJs", function (data, responseCallback) {
document.getElementById("show").innerHTML = '指定接收到Java的数据: ' + data;
var responseData = 'js指定接收完毕,并回传数据给java';
responseCallback(responseData); //回传数据给java
});
})js传递消息给java,两种方式:默认跟spec
js中发送消息,接收callback代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14function jsToJavaDefault() {
var msg = "js发送给java-->默认消息";
window.WebViewJavascriptBridge.send(msg, function (resp) {
document.getElementById("show").innerHTML = resp;
});
}
//js2java-> step1
function jsToJavaSpec() {
var msg = "js发送给java-->指定消息";
window.WebViewJavascriptBridge.callHandler('submitFromWeb', msg, function (resp) {
document.getElementById("show").innerHTML = resp;
});
}java中处理消息,回传callback代码如下:同样两种方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* 初始化jsbridge用于接受js发来的消息,并利用回调将传递数据给js。
*/
private void initJs() {
webView.setDefaultHandler(new BridgeHandler() {
public void handler(String data, CallBackFunction function) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
function.onCallBack("java默认接收完毕,并回传数据给js");
}
});
//js2java-> step2
webView.registerHandler("submitFromWeb", new BridgeHandler() {
public void handler(String data, CallBackFunction function) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
function.onCallBack("java指定接收完毕,并回传数据给js");
}
});
}
源码解析
基础知识
java跟js交互的原始方式
java->js:
- webView.loadUrl(“javascript:functionInJs(msg)”) jsBridge中选择的方式
webView.evaluateJavascript(“javascript:functionInJs(msg)”,callback) API19(4.4)之后都可以,带有callback
js->java:
- 在java声明JsJavaBridge.class,类中的方法用@JavaScriptInterface标记,设置webView.addJavascriptInterface(new JsJavaBridge(),”bridge”)之后,可以在js中使用bridge.xxx()调用JsJavaBridge中的方法,这种方式在4.4之前有安全问题,不建议使用(不过现在基本上不会去适配4.4以下的了)
- 自定义WebViewClient,在shouldOverrideUrlLoading()中拦截url之后调用java代码。(这个是jsbridge中选择的方式)需要注意版本适配 API24 (7.0)
- 自定义WebChromeClient,拦截console之后调用java代码
- 自定义WebChromeClient,拦截弹框事件之后调用Java 代码
- webView.loadUrl(“javascript:functionInJs(msg)”) jsBridge中选择的方式
可以通过重写webviewClient的onPageFinished方法,在方法中通过loadurl的方式给webview注入js代码。
关于H5中的iframe:通过修改iframe的src来触发webViewClient的shouldOverrideUrlLoading方法。
流程分析
先简单描述下整个流程:
首先在webViewClient的onPageFinished的方法里加载了assets中的WebViewJavascriptBridge.js。
java->js :
Java处理消息体:生成一个唯一标示(callbackId)+ msg实体 + functionTag封装成message对象;其中functionTag是js中消息map的key,js可以根据这个找到java希望js调用的方法。
将callbackId作为key,handler作为value放入java中的消息map;(我们给js发一个request,js处理完成之后会有一个response,这个handler就是处理response的)
上面的js消息map需要我们在js中注册,其实就是把functionTag作为key,把回调的方法作为value放入map中。
将message对象通过loadurl的方式传递给js
js收到消息会先判读是否有callbackId(原因在js传递消息给java中),因为我们传递过来了,那么肯定是有的,之后会去判断js的消息map中是否有functionTag这个key(原因在java传递消息给js的默认方式中),因为我们在js中注册过了,那么肯定是有的,然后取出key对应的回调,处理java传过来的msg实体。
将callbackId跟处理完成的结果放入js中的消息队列queue。
之后会修改iframe控件src的url,在url上会拼接上“我有新消息了”,这样就会触发WebViewClient中的shouldOverrideUrlLoading方法,这样java就知道了js消息队列中有新消息了。
java通过loadurl的方式通知js取出js消息队列中的所有消息,搞成jsonString。
修改iframe控件的url,在url上会拼接上“我的所有消息:jsonString”,此时会触发WebViewClient中的shouldOverrideUrlLoading方法,这样java就收到了js消息队列中的所有消息。
最后我们取出消息,根据消息中的callbackId在java的消息map中找到对应的handler,之后处理js的response。
对应的源码流程如下:
1 | //发送消息给js |
进入callHandler方法:
1 | /** |
进入send方法,作用就是上面提到的封装message。并且把callbackId跟处理response的handler放入了responseCallbacks中。
1 | /** |
进入queueMessage方法
1 | /** |
进入dispatchMessage,重点最后一句(将消息传递给了js)。
1 | /** |
进入js的_handleMessageFromNative方法
1 | //提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以 |
进入_dispatchMessageFromNative方法(根据functionTag找到在js中注册处理方法handler并调用)
1 | //提供给native使用, |
handler处理完成之后有个回调responseCallback,会调用_doSend方法。(将java传递过来的callbackid和js处理完成之后的response封装成一个对象作为参数)
进入_doSend方法,将消息放入消息队列。之后修改iframe控件的src。
1 | //sendMessage add message, 触发native处理 sendMessage |
进入WebViewClient的shouldOverrideUrlLoading方法
1 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { |
进入flushMessageQueue方法,其实就调用了一个方法loadUrl,有一个callback(可以在回调的时候再看)
1 | /** |
loadUrl方法:调用js的_fetchQueue方法并把“_fetchQueue”作为key,上面的callback作为value放入responseCallbacks中。
1 | public void loadUrl(String jsUrl, CallBackFunction returnCallback) { |
进入js中的_fetchQueue方法:取出js中所有的消息,放在url的后面
1 | // 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容 |
进入WebViewClient的shouldOverrideUrlLoading方法
1 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { |
进入handlerReturnData方法,根据callbackid在responseCallbacks中找到处理response的handler。执行handler的callback方法。流程结束!!!
1 | /** |
js->java:
跟java->js在本质上没有什么区别,有兴趣可以自己跟一下(如果理解了上面的流程,会很顺利的跟下来的~)。
源码中的一个小问题
源码中BridgeWebView实现了WebViewJavascriptBridge接口,该接口是对外暴露的发送消息给js的接口,源码里面只声明了两种方式(1->默认方式发送消息给js,不带回调 ; 2->默认方式发送消息给js,带回调 ),但是实际上BridgeWebView对外暴露的有3种方式,还有一种(3-> callHandler(spec方式,带回调的)),所以我把callHandler也声明到了接口里。
1 | /** |