Glide项目中有个Jsbridge的包,借着看Glide的机会顺带分析下这个框架。
既然要做到交互,肯定是h5和native都有对应的代码去沟通协调,两个都借助各自的一边代理人完成交互,所谓桥的概念。首先我们先看h5代码,就是一个js文件,这个一般是放在assets里预先在webview中加到加载的html中。
//notation: js file can only use this kind of comments
//since comments will cause error when use in webview.loadurl,
//comments will be remove by java use regexp
(function() {
if (window.WebViewJavascriptBridge) {
return;
}
var messagingIframe;
var bizMessagingIframe;
var sendMessageQueue = [];
var receiveMessageQueue = [];
var messageHandlers = {};
var CUSTOM_PROTOCOL_SCHEME = 'yy';
var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
var responseCallbacks = {};
var uniqueId = 1;
// 创建消息index队列iframe
function _createQueueReadyIframe(doc) {
messagingIframe = doc.createElement('iframe');
messagingIframe.style.display = 'none';
doc.documentElement.appendChild(messagingIframe);
}
//创建消息体队列iframe
function _createQueueReadyIframe4biz(doc) {
bizMessagingIframe = doc.createElement('iframe');
bizMessagingIframe.style.display = 'none';
doc.documentElement.appendChild(bizMessagingIframe);
}
//set default messageHandler 初始化默认的消息线程
function init(messageHandler) {
if (WebViewJavascriptBridge._messageHandler) {
throw new Error('WebViewJavascriptBridge.init called twice');
}
WebViewJavascriptBridge._messageHandler = messageHandler;
var receivedMessages = receiveMessageQueue;
receiveMessageQueue = null;
for (var i = 0; i < receivedMessages.length; i++) {
_dispatchMessageFromNative(receivedMessages[i]);
}
}
// 发送
function send(data, responseCallback) {
_doSend({
data: data
}, responseCallback);
}
// 注册线程 往数组里面添加值
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler;
}
// 调用线程
function callHandler(handlerName, data, responseCallback) {
_doSend({
handlerName: handlerName,
data: data
}, responseCallback);
}
//sendMessage add message, 触发native处理 sendMessage
function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message.callbackId = callbackId;
}
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
// 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
//android can't read directly the return data, so we can reload iframe src to communicate with java
if (messageQueueString !== '[]') {
bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
}
}
//提供给native使用,
function _dispatchMessageFromNative(messageJSON) {
setTimeout(function() {
var message = JSON.parse(messageJSON);
var responseCallback;
//java call finished, now need to call js callback function
if (message.responseId) {
responseCallback = responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
responseCallback(message.responseData);
delete responseCallbacks[message.responseId];
} else {
//直接发送
if (message.callbackId) {
var callbackResponseId = message.callbackId;
responseCallback = function(responseData) {
_doSend({
responseId: callbackResponseId,
responseData: responseData
});
};
}
var handler = WebViewJavascriptBridge._messageHandler;
if (message.handlerName) {
handler = messageHandlers[message.handlerName];
}
//查找指定handler
try {
handler(message.data, responseCallback);
} catch (exception) {
if (typeof console != 'undefined') {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
}
}
}
});
}
//提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
function _handleMessageFromNative(messageJSON) {
console.log(messageJSON);
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON);
}
_dispatchMessageFromNative(messageJSON);
}
var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
init: init,
send: send,
registerHandler: registerHandler,
callHandler: callHandler,
_fetchQueue: _fetchQueue,
_handleMessageFromNative: _handleMessageFromNative
};
var doc = document;
_createQueueReadyIframe(doc);
_createQueueReadyIframe4biz(doc);
var readyEvent = doc.createEvent('Events');
readyEvent.initEvent('WebViewJavascriptBridgeReady');
readyEvent.bridge = WebViewJavascriptBridge;
doc.dispatchEvent(readyEvent);
})();
整个js代码差不多是匿名函数的调用,函数体上面都是各种函数定义,我们找到最下面的初始化代码,一开始是WebViewJavascriptBridge变量的创建,如果你有函数指针的编程经历,那就算不熟悉js,也能看懂,就是把function赋值成员变量。_createQueueReadyIframe 与_createQueueReadyIframe4biz两个方法创建了iframe元素,赋值src时会触发android中 webviewclient --shouldOverrideUrlLoading,一般根据url 中的scheme, path,parameter进行识别处理。后面声明WebViewJavascriptBridgeReady事件并发布事件,js中去注册监听这个事件,完成后续的操作。
var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
init: init,
send: send,
registerHandler: registerHandler,
callHandler: callHandler,
_fetchQueue: _fetchQueue,
_handleMessageFromNative: _handleMessageFromNative
};
var doc = document;
_createQueueReadyIframe(doc);
_createQueueReadyIframe4biz(doc);
var readyEvent = doc.createEvent('Events');
readyEvent.initEvent('WebViewJavascriptBridgeReady');
readyEvent.bridge = WebViewJavascriptBridge;
doc.dispatchEvent(readyEvent);
不重复执行,初始化
if (window.WebViewJavascriptBridge) {
return;
}
messagingIframe 与bizMessagingIframe 的声明与创建,加入document树。
var messagingIframe;
var bizMessagingIframe;
// 创建消息index队列iframe
function _createQueueReadyIframe(doc) {
messagingIframe = doc.createElement('iframe');
messagingIframe.style.display = 'none';
doc.documentElement.appendChild(messagingIframe);
}
//创建消息体队列iframe
function _createQueueReadyIframe4biz(doc) {
bizMessagingIframe = doc.createElement('iframe');
bizMessagingIframe.style.display = 'none';
doc.documentElement.appendChild(bizMessagingIframe);
}
_doSend是发送消息的方法, messagingIframe.src =。。。就会触发 webviewclient–shouldOverrideUrlLoading。bizMessagingIframe.src =。。。类似的, 只不过前者是消息入队,然后通过一个请求,shouldOverrideUrlLoading的处理,native那边再调用h5,调用_fetchQueue方法把消息队列发过去。 这么说吧,h5先对native说,我有你的消息数据。 native听到后说,那行,你把消息队列发给我看看瞧。 h5回复,好的,你别急,我现在就给你传过去。。。。_doSend中responseCallback不为空,生成一个唯一的callbackId,并把回调一起放到responseCallbacks里面,js的键,值结构。sendMessageQueue.push(message)消息入队。_fetchQueue就是把消息队列转化成json字符串发过去,并置空消息队列
//sendMessage add message, 触发native处理 sendMessage
function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message.callbackId = callbackId;
}
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
// 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
//android can't read directly the return data, so we can reload iframe src to communicate with java
if (messageQueueString !== '[]') {
bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
}
}
js调用native的方法,可以理解成发送消息的重载,handlerName就是指名道姓的让谁处理,没有的话是交给native默认函数处理
// 发送
function send(data, responseCallback) {
_doSend({
data: data
}, responseCallback);
}
// 调用线程
function callHandler(handlerName, data, responseCallback) {
_doSend({
handlerName: handlerName,
data: data
}, responseCallback);
}
js自己注册自己能提供的方法名和对应的处理操作,native会根据handlerName,去让js做操作,这个名字是android和h5协商的业务。
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler;
}
_handleMessageFromNatives是native方法在发消息时候调用的,随后在js里面_handleMessageFromNative 调用_dispatchMessageFromNative
//提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
function _handleMessageFromNative(messageJSON) {
console.log(messageJSON);
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON);
}
_dispatchMessageFromNative(messageJSON);
}
这个方法比较长,if (message.responseId) 判断是否是个native处理完成后给h5回复消息,执行回复的调用,调用完后清除。 否则是个新消息,创建回调函数,handler第一次赋值是WebViewJavascriptBridge._messageHandler默认的处理函数,如果native指定了message.handlerName,那就用js自己之前注册的操作处理。
function _dispatchMessageFromNative(messageJSON) {
setTimeout(function() {
var message = JSON.parse(messageJSON);
var responseCallback;
//java call finished, now need to call js callback function
if (message.responseId) {
responseCallback =responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
responseCallback(message.responseData);
delete responseCallbacks[message.responseId];
} else {
//直接发送
if (message.callbackId) {
var callbackResponseId = message.callbackId;
responseCallback = function(responseData) {
_doSend({
responseId: callbackResponseId,
responseData: responseData
});
};
}
var handler = WebViewJavascriptBridge._messageHandler;
if (message.handlerName) {
handler = messageHandlers[message.handlerName];
}
//查找指定handler
try {
handler(message.data, responseCallback);
} catch (exception) {
if (typeof console != 'undefined') {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
}
}
}
});
}
以上就是js代码,粗略的分析完成。下面分析android这边的代码
可以看出这个框架的java代码并不多,三个接口,五个类就可以实现Native 和h5的交互。
回调接口,onCallBack方法入参是data字符串
public interface CallBackFunction {
public void onCallBack(String data);
}
BridgeHandler接口,handler方法入参,第一个是data字符串,第二个是CallBackFunction
public interface BridgeHandler {
void handler(String data, CallBackFunction function);
}
WebViewJavascriptBridge接口,拥有两个重载的send方法,一个有一个data字符串入参,一个是两个入参,data 与responseCallback
public interface WebViewJavascriptBridge {
public void send(String data);
public void send(String data, CallBackFunction responseCallback);
}
Message类源码先贴上,一个数据bean ,主要有五个成员变量,callbackId,responseId,responseData,data,handlerName并且提供了json<–>object的方法
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";
public String getResponseId() {
return responseId;
}
public void setResponseId(String responseId) {
this.responseId = responseId;
}
public String getResponseData() {
return responseData;
}
public void setResponseData(String responseData) {
this.responseData = responseData;
}
public String getCallbackId() {
return callbackId;
}
public void setCallbackId(String callbackId) {
this.callbackId = callbackId;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getHandlerName() {
return handlerName;
}
public void setHandlerName(String handlerName) {
this.handlerName = handlerName;
}
public String toJson() {
JSONObject jsonObject= new JSONObject();
try {
jsonObject.put(CALLBACK_ID_STR, getCallbackId());
jsonObject.put(DATA_STR, getData());
jsonObject.put(HANDLER_NAME_STR, getHandlerName());
jsonObject.put(RESPONSE_DATA_STR, getResponseData());
jsonObject.put(RESPONSE_ID_STR, getResponseId());
return jsonObject.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
public static Message toObject(String jsonStr) {
Message m = new Message();
try {
JSONObject jsonObject = new JSONObject(jsonStr);
m.setHandlerName(jsonObject.has(HANDLER_NAME_STR) ? jsonObject.getString(HANDLER_NAME_STR):null);
m.setCallbackId(jsonObject.has(CALLBACK_ID_STR) ? jsonObject.getString(CALLBACK_ID_STR):null);
m.setResponseData(jsonObject.has(RESPONSE_DATA_STR) ? jsonObject.getString(RESPONSE_DATA_STR):null);
m.setResponseId(jsonObject.has(RESPONSE_ID_STR) ? jsonObject.getString(RESPONSE_ID_STR):null);
m.setData(jsonObject.has(DATA_STR) ? jsonObject.getString(DATA_STR):null);
return m;
} catch (JSONException e) {
e.printStackTrace();
}
return m;
}
public static List<Message> toArrayList(String jsonStr){
List<Message> list = new ArrayList<Message>();
try {
JSONArray jsonArray = new JSONArray(jsonStr);
for(int i = 0; i < jsonArray.length(); i++){
Message m = new Message();
JSONObject jsonObject = jsonArray.getJSONObject(i);
m.setHandlerName(jsonObject.has(HANDLER_NAME_STR) ? jsonObject.getString(HANDLER_NAME_STR):null);
m.setCallbackId(jsonObject.has(CALLBACK_ID_STR) ? jsonObject.getString(CALLBACK_ID_STR):null);
m.setResponseData(jsonObject.has(RESPONSE_DATA_STR) ? jsonObject.getString(RESPONSE_DATA_STR):null);
m.setResponseId(jsonObject.has(RESPONSE_ID_STR) ? jsonObject.getString(RESPONSE_ID_STR):null);
m.setData(jsonObject.has(DATA_STR) ? jsonObject.getString(DATA_STR):null);
list.add(m);
}
} catch (JSONException e) {
e.printStackTrace();
}
return list;
}
}
DefaultHandler提供了一个默认的hander行为
public class DefaultHandler implements BridgeHandler{
String TAG = "DefaultHandler";
@Override
public void handler(String data, CallBackFunction function) {
if(function != null){
function.onCallBack("DefaultHandler response data");
}
}
}
BridgeUtil是一个工具类,提供了一些URL中参数的抽取方法,webViewLoadJs提供了在h5执行js的方法
public class BridgeUtil {
final static String YY_OVERRIDE_SCHEMA = "yy://";
final static String YY_RETURN_DATA = YY_OVERRIDE_SCHEMA + "return/";//格式为 yy://return/{function}/returncontent
final static String YY_FETCH_QUEUE = YY_RETURN_DATA + "_fetchQueue/";
final static String EMPTY_STR = "";
final static String UNDERLINE_STR = "_";
final static String SPLIT_MARK = "/";
final static String CALLBACK_ID_FORMAT = "JAVA_CB_%s";
final static String JS_HANDLE_MESSAGE_FROM_JAVA = "javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');";
final static String JS_FETCH_QUEUE_FROM_JAVA = "javascript:WebViewJavascriptBridge._fetchQueue();";
public final static String JAVASCRIPT_STR = "javascript:";
public static String parseFunctionName(String jsUrl){
return jsUrl.replace("javascript:WebViewJavascriptBridge.", "").replaceAll("\\(.*\\);", "");
}
public static String getDataFromReturnUrl(String url) {
if(url.startsWith(YY_FETCH_QUEUE)) {
return url.replace(YY_FETCH_QUEUE, EMPTY_STR);
}
String temp = url.replace(YY_RETURN_DATA, EMPTY_STR);
String[] functionAndData = temp.split(SPLIT_MARK);
if(functionAndData.length >= 2) {
StringBuilder sb = new StringBuilder();
for (int i = 1; i < functionAndData.length; i++) {
sb.append(functionAndData[i]);
}
return sb.toString();
}
return null;
}
public static String getFunctionFromReturnUrl(String url) {
String temp = url.replace(YY_RETURN_DATA, EMPTY_STR);
String[] functionAndData = temp.split(SPLIT_MARK);
if(functionAndData.length >= 1){
return functionAndData[0];
}
return null;
}
/**
* js 文件将注入为第一个script引用
* @param view
* @param url
*/
public static void webViewLoadJs(WebView view, String url){
String js = "var newscript = document.createElement(\"script\");";
js += "newscript.src=\"" + url + "\";";
js += "document.scripts[0].parentNode.insertBefore(newscript,document.scripts[0]);";
view.loadUrl("javascript:" + js);
}
public static void webViewLoadLocalJs(WebView view, String path){
String jsContent = assetFile2Str(view.getContext(), path);
view.loadUrl("javascript:" + jsContent);
}
public static String assetFile2Str(Context c, String urlStr){
InputStream in = null;
try{
in = c.getAssets().open(urlStr);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
String line = null;
StringBuilder sb = new StringBuilder();
do {
line = bufferedReader.readLine();
if (line != null && !line.matches("^\\s*\\/\\/.*")) {
sb.append(line);
}
} while (line != null);
bufferedReader.close();
in.close();
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
return null;
}
}
消息传递的拆包动作,解析出需要的数据,函数名,数据,回复消息中的函数名
public static String parseFunctionName(String jsUrl){
public static String getDataFromReturnUrl(String url) {
public static String getFunctionFromReturnUrl(String url) {
JS_FETCH_QUEUE_FROM_JAVA与JS_HANDLE_MESSAGE_FROM_JAVA都是native 去调用js的方法,前者是js通知native有消息后,native发起的取消息调用。后者是native主动发消息给js,让他们做对应的处理。
final static String JS_HANDLE_MESSAGE_FROM_JAVA = "javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');";
final static String JS_FETCH_QUEUE_FROM_JAVA = "javascript:WebViewJavascriptBridge._fetchQueue();";
这两个静态方法作用就是读取本地的js加载到webview要加载的html中
public static void webViewLoadJs(WebView view, String url){
public static String assetFile2Str(Context c, String urlStr){
BridgeWebView核心view的源码贴上
@SuppressLint("SetJavaScriptEnabled")
public class BridgeWebView extends WebView implements WebViewJavascriptBridge {
private final String TAG = "BridgeWebView";
public static final String toLoadJs = "WebViewJavascriptBridge.js";
Map<String, CallBackFunction> responseCallbacks = new HashMap<String, CallBackFunction>();
Map<String, BridgeHandler> messageHandlers = new HashMap<String, BridgeHandler>();
BridgeHandler defaultHandler = new DefaultHandler();
private List<Message> startupMessage = new ArrayList<Message>();
public List<Message> getStartupMessage() {
return startupMessage;
}
public void setStartupMessage(List<Message> startupMessage) {
this.startupMessage = startupMessage;
}
private long uniqueId = 0;
public BridgeWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BridgeWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public BridgeWebView(Context context) {
super(context);
init();
}
/**
*
* @param handler
* default handler,handle messages send by js without assigned handler name,
* if js message has handler name, it will be handled by named handlers registered by native
*/
public void setDefaultHandler(BridgeHandler handler) {
this.defaultHandler = handler;
}
private void init() {
this.setVerticalScrollBarEnabled(false);
this.setHorizontalScrollBarEnabled(false);
this.getSettings().setJavaScriptEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
this.setWebViewClient(generateBridgeWebViewClient());
}
protected BridgeWebViewClient generateBridgeWebViewClient() {
return new BridgeWebViewClient(this);
}
void handlerReturnData(String url) {
String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
CallBackFunction f = responseCallbacks.get(functionName);
String data = BridgeUtil.getDataFromReturnUrl(url);
if (f != null) {
f.onCallBack(data);
responseCallbacks.remove(functionName);
return;
}
}
@Override
public void send(String data) {
send(data, null);
}
@Override
public void send(String data, CallBackFunction responseCallback) {
doSend(null, data, responseCallback);
}
private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
Message m = new Message();
if (!TextUtils.isEmpty(data)) {
m.setData(data);
}
if (responseCallback != null) {
String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
responseCallbacks.put(callbackStr, responseCallback);
m.setCallbackId(callbackStr);
}
if (!TextUtils.isEmpty(handlerName)) {
m.setHandlerName(handlerName);
}
queueMessage(m);
}
private void queueMessage(Message m) {
if (startupMessage != null) {
startupMessage.add(m);
} else {
dispatchMessage(m);
}
}
void dispatchMessage(Message m) {
String messageJson = m.toJson();
//escape special characters for json string
messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");
messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");
String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
this.loadUrl(javascriptCommand);
}
}
void flushMessageQueue() {
if (Thread.currentThread() ==Looper.getMainLooper().getThread()) {
loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
@Override
public void onCallBack(String data) {
// deserializeMessage
List<Message> list = null;
try {
list = Message.toArrayList(data);
} catch (Exception e) {
e.printStackTrace();
return;
}
if (list == null || list.size() == 0) {
return;
}
for (int i = 0; i < list.size(); i++) {
Message m = list.get(i);
String responseId = m.getResponseId();
// 是否是response
if (!TextUtils.isEmpty(responseId)) {
CallBackFunction function=responseCallbacks.get(responseId);
String responseData = m.getResponseData();
function.onCallBack(responseData);
responseCallbacks.remove(responseId);
} else {
CallBackFunction responseFunction = null;
// if had callbackId
final String callbackId = m.getCallbackId();
if (!TextUtils.isEmpty(callbackId)) {
responseFunction = new CallBackFunction() {
@Override
public void onCallBack(String data) {
Message responseMsg = new Message();
responseMsg.setResponseId(callbackId);
responseMsg.setResponseData(data);
queueMessage(responseMsg);
}
};
} else {
responseFunction = new CallBackFunction() {
@Override
public void onCallBack(String data) {
// do nothing
}
};
}
BridgeHandler handler;
if (!TextUtils.isEmpty(m.getHandlerName())) {
handler = messageHandlers.get(m.getHandlerName());
} else {
handler = defaultHandler;
}
if (handler != null){
handler.handler(m.getData(), responseFunction);
}
}
}
}
});
}
}
public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
this.loadUrl(jsUrl);
responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback);
}
public void registerHandler(String handlerName, BridgeHandler handler) {
if (handler != null) {
messageHandlers.put(handlerName, handler);
}
}
public void callHandler(String handlerName, String data,CallBackFunction callBack) {
doSend(handlerName, data, callBack);
}
}
这个方法跟js 那边的差不多,遍历消息列表,然后判断是回复消息,执行处理,清除。 新消息就看他的handlerName,native有注册的就调用,否则默认,如果存在callBackId就保留回调。回复回调中消息入队,被执行时,发送回复消息给js
void flushMessageQueue() {
处理回复的数据,并且responseCallbacks中删除
void handlerReturnData(String url) {
String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
CallBackFunction f = responseCallbacks.get(functionName);
String data = BridgeUtil.getDataFromReturnUrl(url);
if (f != null) {
f.onCallBack(data);
responseCallbacks.remove(functionName);
return;
}
}
直接发送消息,这种一般交给js默认处理函数处理
@Override
public void send(String data) {
send(data, null);
}
@Override
public void send(String data, CallBackFunction responseCallback) {
doSend(null, data, responseCallback);
}
创建消息,并在responseCallbacks保存回复回调,消息入队
private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
Message m = new Message();
if (!TextUtils.isEmpty(data)) {
m.setData(data);
}
if (responseCallback != null) {
String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
responseCallbacks.put(callbackStr, responseCallback);
m.setCallbackId(callbackStr);
}
if (!TextUtils.isEmpty(handlerName)) {
m.setHandlerName(handlerName);
}
queueMessage(m);
}
startupMessage消息会在onPageFinished中处理完之后会被置空,后面的入队消息直接dispatchMessage(m);
private void queueMessage(Message m) {
if (startupMessage != null) {
startupMessage.add(m);
} else {
dispatchMessage(m);
}
}
dispatchMessage就是通过this.loadUrl发送消息给js执行
void dispatchMessage(Message m) {
String messageJson = m.toJson();
//escape special characters for json string
messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");
messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");
String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
this.loadUrl(javascriptCommand);
}
native自己注册能提供给h5调用的一些处理
public void registerHandler(String handlerName, BridgeHandler handler) {
if (handler != null) {
messageHandlers.put(handlerName, handler);
}
}
我们最常用的方法,发消息给js,handlerName:方法名,data:数据,callBack:回复回调函数
public void callHandler(String handlerName, String data, CallBackFunction callBack) {
doSend(handlerName, data, callBack);
}
BridgeWebViewClient源码贴上
public class BridgeWebViewClient extends WebViewClient {
private BridgeWebView webView;
public BridgeWebViewClient(BridgeWebView webView) {
this.webView = webView;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
try {
url = URLDecoder.decode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) { // 如果是返回数据
webView.handlerReturnData(url);
return true;
} else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) { //
webView.flushMessageQueue();
return true;
} else {
return super.shouldOverrideUrlLoading(view, url);
}
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (BridgeWebView.toLoadJs != null) {
BridgeUtil.webViewLoadLocalJs(view, BridgeWebView.toLoadJs);
}
//
if (webView.getStartupMessage() != null) {
for (Message m : webView.getStartupMessage()) {
webView.dispatchMessage(m);
}
webView.setStartupMessage(null);
}
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
}
}
这是h5返回的消息数据处理
if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) { // 如果是返回数据
webView.handlerReturnData(url);
***这是h5要native去取消息 ***
if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) { //
webView.flushMessageQueue();
在最新的版本中,作者又加了一个custom开头的处理函数,优先级比下面的这行高,让程序员自己定义自己要处理的其他业务scheme.比如:支付宝,微信,建行。。。等等
else {
return super.shouldOverrideUrlLoading(view, url);
}
页面加载完成,填充jsbridge,把一些预先设置的StartupMessage先行入队,之后才是其他消息的发送
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (BridgeWebView.toLoadJs != null) {
BridgeUtil.webViewLoadLocalJs(view, BridgeWebView.toLoadJs);
}
//
if (webView.getStartupMessage() != null) {
for (Message m : webView.getStartupMessage()) {
webView.dispatchMessage(m);
}
webView.setStartupMessage(null);
}
}
分析完毕