WebViewJavascriptBridge.js代码学习
//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
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);
})();
待过的某家公司中用于和原生交互的方案,当初这个文件也是从网上找的,拿来就用,没怎么细看。
最早我们的Hybrid应用只是h5调用原生的一些方法,所以只使用了native向页面注入一个webview的变量,并在这个变量上挂载一些方法,在安卓上存在一些问题(?安全漏洞)。后来改用bridge,安全性和兼容性比较好。bridge有一个小小的问题就是如果原生端不存在对应名称的方法,h5无法知道,所以h5需要做一套版本的判断,再进行方法的调用。
执行的操作
简单分析一下:这个文件包含了一个立即执行函数,代码加载后,做了几件事情:
- 定义协议scheme,类似http,这里定义为yy
- 创建消息index队列iframe,这个iframe后面可以看到是h5端使用的;创建消息体队列iframe,这个iframe可以看到是给原生端使用的。这两个iframe用于消息传递。
- 创建WebViewJavaScriptBridge对象并挂载到window上
- 创建Event对象,初始化事件名为WebViewJavaScriptBridgeReady的事件,并触发事件,表示bridge对象已准备好,可以使用了。
bridge对象
bridge对象有6个方法,分别为init、send、registerHandler、callHandler、_fetchQueue、_handleMessageFromNative,方法的作用大致可以见名知意。
- init方法,用于设置默认的消息处理函数,如果此时h5端的消息接收队列不为空,则调用_dispatchMessageFromNative来分发处理队列中的消息
- send方法,用于h5端发送消息,触发native去处理,调用_doSend将消息塞进h5端的消息发送队列,并将messagingIframe的src设置为发送的协议,触发iframe重新请求加载
- registerHandler方法:用于定义消息名称与消息回调函数的映射,保存在messageHandlers对象上,提供js函数给native调用,在原生完成操作后,将数据通过回调函数传递给h5
- callHandler方法:用于h5调用_doSend修改iframe src的方式将消息和数据传递原生
- _fetchQueue方法:提供给native使用的方法,将bizMessagingIframe的src设置为接收的协议,触发iframe重新请求,使原生获取到h5发送的消息
- _handleMessageFromNative方法:提供给native使用的方法,将消息塞进h5的消息接收队列,并进行消息分发
大致流程
1. H5调用Native
2. Native向H5传递
原理
H5调用Native的流程主要利用了iframe与原生端进行交互,通过修改iframe的src来触发原生端调用对应的操作,原生端监听到触发后,经过一系列操作后得到一个结果,再通过调用h5的回调函数,使h5可以处理原生返回的结果。
Native向h5传递有点类似h5的事件监听,h5注册一个名称用于标记操作,映射一个回调函数,原生端在用户做了某些操作后,获取到h5注册的操作对应的回调函数,并进行调用,将操作结果传递给h5。
参考
https://github.com/lzyzsd/JsBridge
luffyjet/WebViewJavaScriptBridge
marcuswestin/WebViewJavascriptBridge
https://blog.csdn.net/weixin_42130889/article/details/112208942
https://blog.csdn.net/sinat_33488770/article/details/120071214
WebViewJavascriptBridge.js代码学习的更多相关文章
- 常用js代码学习
1.用JS实现的radio图片选择按钮效果 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &q ...
- js代码学习
运算符: 复杂运算符:Math.pow(2,53) //=>9007192145641435:2的53次幂 Math.round(.6) //=>1.0:四舍五入 Math.ceil ...
- js学习笔记-编写高效、规范的js代码-Tom
编写高效.规范的js代码: 1.变量命名空间问题,尽量使用局部变量,防止命名冲突(污染作用域中的全局变量):全局空间命名的变量可以在对应的文档域任意位置中使用window调用. 2.尽量使用单var定 ...
- Android学习笔记_32_通过WebView实现JS代码与Java代码互相通信
webview两种实现方法,覆盖onKeyDown()方法 缓存 WebSettings应用注意的几个问题 1.要实现JS代码与Java代码互相通信,需要通过Android的WebView控件,在视图 ...
- JavaScript学习总结(10)——实用JS代码大全
事件源对象 event.srcElement.tagName event.srcElement.type 捕获释放 event.srcElement.setCapture(); event. ...
- 【JS学习】-利用谷歌浏览器调试JS代码(转)
谷歌浏览器是常用来调试JS代码的工具,本文主要介绍如何利用谷歌浏览器来调试JS代码,协助我们进行开发工作. 首先,打开谷歌浏览器,按快捷键F12或者ctrl+shift+j,就可以打开谷歌浏览器的开发 ...
- JS学习十四天----server端运行JS代码
server端运行JS代码 话说,当今不在client使用JS代码才是稀罕事.因为web应用的体验越来越丰富,client用JS实现的逻辑也越来越多,这造成的结果就是某些差点儿一致的逻辑须要在clie ...
- js入门学习~ 运动应用小例
要实现的效果如下: 鼠标移入各个小方块,实现对应的效果(变宽,变高,移入透明,移出恢复)~~ (且各运动相互之前不干扰) 主要是练习多个物体的运动框架~~ --------------------- ...
- JS入门学习,写一个时钟~
<!-- 耽搁了几天,于是又继续回到JS的学习了~~ 各种头大,加油吧... --> <!doctype html><html><head> <t ...
- Node.js入门学习笔记(一)
先来个最常见的"Hello World!". 打开你最喜欢的编辑器(我用的是Sublime Text),创建一个helloWorld.js的文件.我们要做的就是向stdout输出& ...
随机推荐
- Hugging News #0731: 新课程重磅发布、用户交流群邀请你加入、真实图像编辑方法 LEDTIS 来啦!
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...
- Docker本地搭建个人企业私有云盘seafile搭建(完美解决ONLYOFFICE无法预览的情况)
seafile搭建 #创建存放路径 mkdir -p /media/megrez/data/seafile/seafile-mysql/db mkdir -p /media/megrez/data/s ...
- 26种source-map看花了眼?别急,理解这几个全弄懂
上一篇 webpack处理模块化源码 的文章中提到了 "source map",这一篇来详细说说. 有什么作用 source map 用于映射编译后的代码与源码,这样如果编译后的代 ...
- [minio]挂载minio到本地
前言 将minio的bucket挂载到本地文件系统 环境 客户端系统版本:centos 7 MinIO节点IP:192.168.0.20 s3fs方式步骤 安装s3fs客户端(可能需要先安装epel- ...
- Eolink 出席 QECon 大会,引领「AI+API」技术的革新浪潮
7月28日-29日,第八届 QECon 质量效能大会在北京成功召开.大会聚焦"数生智慧,高质量发展新引擎",深入探讨如何利用数字化和智能化技术推动软件质量的发展,进而为高质量的经济 ...
- 使用LabVIEW 实现物体识别、图像分割、文字识别、人脸识别等深度视觉
前言 哈喽,各位朋友们,这里是virobotics(仪酷智能),这两天有朋友私信问之前给大家介绍的工具包都可以实现什么功能,最新的一些模型能否使用工具包加载,今天就给大家介绍一下博主目前使用工具包已经 ...
- salesforce零基础学习(一百三十)Report 学习进阶篇
本篇参考: https://help.salesforce.com/s/articleView?id=sf.reports_summary_functions_about.htm&type=5 ...
- 《深入理解Java虚拟机》读书笔记: 类加载器
类加载器 虚拟机设计团队把类加载阶段中的"通过一个类的全限定名来获取描述 ...
- 【pandas小技巧】--DataFrame的显示样式
上一篇介绍了DataFrame的显示参数,主要是对DataFrame中值进行调整. 本篇介绍DataFrame的显示样式的调整,显示样式主要是对表格本身的调整,比如颜色,通过颜色可以突出显示重要的值, ...
- GIT提交修改的项目到远程仓库
1.在项目目录下右键选择Git Bash. 2.执行提交命令三部曲 git add . //文件-暂存区,即将所有新增的文件添加到提交索引中,,add后面是"空格 点"就表示当前目 ...