WebViewJavascriptBridge工程结构和源码解析
来源:互联网 发布:荣威950 知乎 编辑:程序博客网 时间:2024/06/16 22:56
WebViewJavascriptBridge
javaScript调用Native其实是用重定向拼成url参数传递给Native
Native根据重定向的地址调用指定名称的回调函数
javascript生成url然后重定向使webview捕捉到重定向事件,解析重定向的过程。
shouldOverrideUrlLoading
Native调用javascript也是一样,实现用js代码注册好,
然后将指定参数名和回调函数转成可以供webview调用字符串格式。
实际就是调用webview.loadUrl
运行时结构如图
这里我简明扼要的的打个比喻
你拿银行卡(数据)去取钱(调用网页的window.WebViewJavascriptBridge.send方法)并给你一个小票(callBackID),大堂经理(android端) 给了
你一个pos机( 网页加载完成后注入WebViewJavaScriptBridge.js),pos机(WebViewJavaScriptBridge.js)和银行(android 端事先注册的一些监听重定向连接的方法)
之间交互之后通过小票(callBackid)告诉你取钱成功了。
为什么安全的原因是
1:pos机和银行交互
对你是不透明,无法拦截无法调用。
2:pos机 无法伪造(网页加载完成后注入WebViewJavaScriptBridge.js) 是androd给你的
你不知道WebViewJavaScriptBridge.js里面的这2个校验
var CUSTOM_PROTOCOL_SCHEME = 'yy';
var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/'
以下是源码可以看的比较头晕。
从网站上下载工程:https://github.com/lzyzsd/JsBridge
我们来一步一步解析WebViewJavascriptBridge的框架结构,首先
BridgeWebView继承了webview
BridgeWebView设置WebViewClient为BridgeWebViewClient
BridgeWebViewClient有一个重要的时间在网页加载完成时注入js-》WebViewJavaScriptBridge.js
BridgeUtil.webViewLoadLoaclJs(View,BridgeWebView.toLoadJs)
@Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); if (BridgeWebView.toLoadJs != null) { BridgeUtil.webViewLoadLocalJs(view, BridgeWebView.toLoadJs); } ..... }让我们看看assets的
WebViewJavascriptBridge.js的源码怎么写的
WebViewJavascriptBridge基本属性和初始化的几个方法
你点击了发消息给Nativie这个按钮调用如下方法var WebViewJavascriptBridge = window.WebViewJavascriptBridge = { init: init, send: send, registerHandler: registerHandler, callHandler: callHandler, _fetchQueue: _fetchQueue, _handleMessageFromNative: _handleMessageFromNative }; var doc = document; _createQueueReadyIframe(doc); var readyEvent = doc.createEvent('Events'); readyEvent.initEvent('WebViewJavascriptBridgeReady'); readyEvent.bridge = WebViewJavascriptBridge; doc.dispatchEvent(readyEvent);function testClick() { var str1 = document.getElementById("text1").value; var str2 = document.getElementById("text2").value; //send message to native var data = {id: 1, content: "这是一个图片 <img src=\"a.png\"/> test\r\nhahaha"}; window.WebViewJavascriptBridge.send( data , function(responseData) { document.getElementById("show").innerHTML = "repsonseData from java, data = " + responseData } );}这里window.WebViewJavascriptBridge.send指的是send function此时调用function send(data, responseCallback) { _doSend({ data: data }, responseCallback); }WebViewJavascriptBridge.js中的WebViewJavascriptBridge的doSend方法//sendMessage add message, 触发native处理 sendMessage function _doSend(message, responseCallback) { if (responseCallback) {//如果回调函数不为空,为回调函数创建一个CallbackId var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime(); responseCallbacks[callbackId] = responseCallback; message.callbackId = callbackId; } sendMessageQueue.push(message); messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; }此时会发起一个重定向yy://_QUEUE_MESSAGE_/消息然后由webview 捕捉网页重定向事件调用已注册的参数和对应的handler
url.startsWith(BridgeUtil.YY_RETURN_DATA 对应 yy://_QUEUE_MESSAGE_/url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA 对应 <span style="font-family: Menlo; background-color: rgb(255, 255, 255);">yy://return/_fetchQueue/[{"data":{"id":1,"content":"这是一个图片 <img src=\"a.png\"/> test\r\nhahaha"},"callbackId":"cb_1_1464075966655"}]@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { try { url = URLDecoder.decode(url, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } Toast.makeText(view.getContext(), url, Toast.LENGTH_LONG).show(); 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); } }webView.handlerReturnData(url);的实际作用是一次性调用<span style="font-family: Menlo; font-size: 12pt; background-color: rgb(255, 255, 255);">_fetchQueue然后移除</span><pre name="code" class="javascript">void handlerReturnData(String url) {String functionName = BridgeUtil.getFunctionFromReturnUrl(url);System.out.println("functionName:"+functionName);CallBackFunction f = responseCallbacks.get(functionName);String data = BridgeUtil.getDataFromReturnUrl(url);if (f != null) {f.onCallBack(data);responseCallbacks.remove(functionName);return;}}调用javacript的_fetchQueuefunction _fetchQueue() { alert("_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 messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString); }然后会重定向这个新的url yy://return/_fetchQueue/[{"data":{"id":1,"content":"这是一个图片 <img src=\"a.png\"/> test\r\nhahaha"},"callbackId":"cb_1_1464075966655"}]
拼接回调函数和数据转成字符串由webview调用,格式和代码如下
void flushMessageQueue() {if (Thread.currentThread() == Looper.getMainLooper().getThread()) {loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {@Overridepublic void onCallBack(String data) {// deserializeMessageList<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();// 是否是responseif (!TextUtils.isEmpty(responseId)) {CallBackFunction function = responseCallbacks.get(responseId);String responseData = m.getResponseData();function.onCallBack(responseData);responseCallbacks.remove(responseId);} else {CallBackFunction responseFunction = null;// if had callbackIdfinal String callbackId = m.getCallbackId();if (!TextUtils.isEmpty(callbackId)) {responseFunction = new CallBackFunction() {@Overridepublic void onCallBack(String data) {Message responseMsg = new Message();responseMsg.setResponseId(callbackId);responseMsg.setResponseData(data);queueMessage(responseMsg);}};} else {responseFunction = new CallBackFunction() {@Overridepublic 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);}}}}});}}
关键核心代码:
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); } }
javascript:WebViewJavascriptBridge._handleMessageFromNative('{\"responseData\":\"DefaultHandler response data\",\"responseId\":\"cb_1_1464076358507\"}');在javascript中完成回调(取出根据回调函数的callbackId,获得引用并调用)//提供给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); } } } }); }
- WebViewJavascriptBridge工程结构和源码解析
- WebViewJavaScriptBridge源码解析
- WebViewJavascriptBridge源码解析 -1
- WebViewJavaScriptBridge源码解析(一)
- iOS源码解析—WebViewJavascriptBridge
- WebViewJavascriptBridge 源码
- WebViewJavascriptBridge 解析
- WebViewJavascriptBridge解析
- WebViewJavascriptBridge解析
- WebViewJavascriptBridge源码探究--看OC和JS交互过程(介绍了WebViewJavascriptBridge的实现过程)
- JS和Native交互之 -WebViewJavascriptBridge源码分析
- WebViewJavascriptBridge源码探究--看OC和JS交互过程
- Android工程结构解析
- WebViewJavascriptBridge源码解读
- WebViewJavascriptBridge源码分析
- WebViewJavascriptBridge源码解读
- geth结构解析和源码分析
- Curator源码解析(一)源码结构和测试程序
- C,C++宏中#与##的讲解
- Java数组详解
- 面试必备-快速排序(Java)
- Android 反编译、Smali插桩、二次打包
- android之MTP框架和流程分析
- WebViewJavascriptBridge工程结构和源码解析
- textView结合selector点击改变文字颜色
- hive sql 语句学习
- s3cmd 操作手册
- Setting下面增加搜索(SearchIndexProvider)
- 数据结构 --- 栈和队列
- 【例题】【动规】NKOJ 1796 数字金字塔
- ? extends T 与 ? super T
- 免费ARP简单介绍和程序编写