JsBridge源码分析
来源:互联网 发布:mask rcnn tensorflow 编辑:程序博客网 时间:2024/06/07 20:04
一.项目介绍
jsbridege是一个开源的项目。主要实现webview和native之间的交互。是支撑Hybrid模式非常重要的基础组件。
基本上各个大厂都有自己的SDK。比如腾讯,美团,携程等等,一些公司也在向外输出自己的专业能力。但是基本上原理都差不多,不过大厂的SDK在兼容性、功能性、安全性上更好一些。
项目地址:
https://github.com/lzyzsd/JsBridge
二.JsBridge原理
在 Js 和 WebView 交互的过程中,主要实现两个方向可以通信即可。
WebView 向 Js 传递数据是通过 WebView.loadUrl(String url) 实现的
在 WebView 中接收 Js 传递的数据是通过 WebViewClient 中的 shouldOverrideUrlLoading(WebView view, String url) 拦截加载链接 String url 参数实现的。
三.JsBridge简单用法
public class JSBridgeActivity extends AppCompatActivity { private BridgeWebView mBridgeWebView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_jsbridge); initView(); } private void initView() { findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); //写一个最简单的双向的调用 //jsbridge目的就是为了实现webview和native的通信。 mBridgeWebView = (BridgeWebView) findViewById(R.id.webView); mBridgeWebView.loadUrl("file:///android_asset/demo.html"); jsSendToNative(); //java传递给前端js } private void jsSendToNative() { //前端传递给java: //点击前端对应的按钮:DefaultHandler方式,调用的代码如下: //前端对应的代码///*用来展示默认方式*/// function testClick() {// var str1 = document.getElementById("text1").value;// var str2 = document.getElementById("text2").value;// //send message to native// var data = {id: 1, content: "我是内容哦"};// window.WebViewJavascriptBridge.send(// data// , function(responseData) {// document.getElementById("show").innerHTML = "data = " + responseData// }// );// } mBridgeWebView.setDefaultHandler(new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { //data是js返回的数据 Toast.makeText(JSBridgeActivity.this, data, Toast.LENGTH_LONG).show(); } }); }}
查看更多demo可移步下面的博客链接。
Android之利用JSBridge库实现Html,JavaScript与Android的所有交互
http://www.cnblogs.com/zhangqie/p/6724252.html
四.JsBridge总体设计
客户端调用JavaSript
web前端调用native
五.demo流程源码分析
运行环境设计java和js两个部分,调用流程在2部分之间交互。需要移动端同学了解一些js语法。
这里具体看一下demo中点击html中的DefaultHandler方式按钮后,js里的数据怎么传递给Android native的。
///*用来展示默认方式*/// function testClick() {// var str1 = document.getElementById("text1").value;// var str2 = document.getElementById("text2").value;// //send message to native// var data = {id: 1, content: "我是内容哦"};// window.WebViewJavascriptBridge.send(// data// , function(responseData) {// document.getElementById("show").innerHTML = "data = " + responseData// }// );// }
1.js:中的testClick调用到WebViewJavascriptBridge.js中的send方法
function send(data, responseCallback) { _doSend({ 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; }
这里调用到doSend,将消息存放在sendMessageQueue中,将responseCallback放在responseCallbacks数组中。
这里着重关注几点:
1.message的callbackId.callbackId由uniqueId配合时间生成,用于后续查找responseCallback回调。callbackId用于html页面中send方法的回调。
2.更换iFrame的src,触发BridgeWebViewClient的shouldOverrideUrlLoading方法。
3.更换src,前缀为yy://QUEUE_MESSAGE/。
2.webview:匹配到shouldOverrideUrlLoading,进入到BridgeWebView的flushMessageQueue方法
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); } } } } }); } }
flushMessageQueue通过主要调用到了loadUrl方法:
public void loadUrl(String jsUrl, CallBackFunction returnCallback) { this.loadUrl(jsUrl); responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback); }
loadUrl方法:
1.首先会去调用BridgeWebView的loadUrl方法。去执行javascript:WebViewJavascriptBridge._fetchQueue()js语句调用。
2.注册了一个回调函数。还将对应的回调函数放在responseCallbacks中, key是_fetchQueue。
3.js:_fetchQueue方法
// 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容 function _fetchQueue() { var messageQueueString = JSON.stringify(sendMessageQueue); sendMessageQueue = []; //add by hq if (isIphone()) { return messageQueueString; //android can't read directly the return data, so we can reload iframe src to communicate with java } else if (isAndroid()) { messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString); } }
这里讲sendMessageQueue数组中的所有消息,序列化为json字符串,通过更改iFrame的src,触发shouldOverrideUrlLoading方法。
4.webview:handlerReturnData方法
将关注点放到shouldOverrideUrlLoading里去调用到的是handlerReturnData方法。
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; } }
这里就和步骤2对应上了。即webview loadjs的_fetchQueue后,fetchqueue的返回结果会被传递到 onCallback的参数data中:
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。即java发消息给web时,传入的回调函数 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(); //即web端发消息给native时,注册的回调函数。需要通过native->js触发 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 handler = defaultHandler; } if (handler != null){ handler.handler(m.getData(), responseFunction); } } } } }
这里算是比较核心的部分了。
这里会调用到handler的handle方法。
即调用到了handler的handle方法中。
这里只是分析了在web上点击一个按钮传递数据给native的情况。
六.模块分析
1.BridgeWebViewClient
是在BridgeWebView构造方法调用了init,init主要设置了
webviewclient = new 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); }}
代码中有几个要点需要关注:
1.onPageFinished方法中会去调用webview的loadUrl方法加载一段js代码:WebViewJavascriptBridge.js.
2.shouldOverrideUrlLoading方法中,会去匹配url的起始,分别调用不同的方法。
七.阅读体会&优缺点&改进意见
iframe的问题
http://blog.csdn.net/u014099894/article/details/72673438
js简单学习
iframe也称作嵌入式框架,嵌入式框架和框架网页类似,它可以把一个网页的框架和内容嵌入在现有的网页中。iframe用于设置文本或图形的浮动图文框或容器。
修改iframe的src会调用webview的shouldOverrideUrlLoading方法。
参考资料
JSbridge系列解析(四):Web端发消息给Native代码流程具体分析
http://cdn2.jianshu.io/p/730eaba1a617
JsBridge源码解析
http://lijiankun24.com/JsBridge%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
- JsBridge 源码分析
- Android JsBridge源码分析
- JsBridge源码分析
- Android webView与js 交互以及jsbridge框架源码分析
- Native JsBridge源码解析 深入理解JsBridge
- 理解JSBridge
- Hybird App 之 JSBridge
- 框架使用系列--JSbridge
- HyBrid应用-JsBridge
- JsBridge与客户端交互
- Hybrid 开发:JsBridge
- JsBridge实现及原理
- JSBridge框架学习小结
- 通过Prompt实现JSBridge
- Android混合开发JSBridge
- 源码分析
- 源码分析
- 源码分析
- STM32F407ZGT6学习笔记(1)
- Kotlin返回值类型简介
- Notepad++ 正则表达式 中文处理
- 论文阅读笔记1
- Solr vs. Elasticsearch谁是开源搜索引擎王者。
- JsBridge源码分析
- Linux中安装JDK
- windows -- 怎么创建一个网页快捷方式
- Django 中文本地化汉化方法
- 从JMSBytesMessage读取数据
- 文件目录损坏
- CUDA零拷贝内存(zerocopy memory)
- Opencv图像处理之滤波降噪
- 在js中实现list<Map>