【quickhybrid】JSBridge的实现
前言
本文介绍quick hybrid
框架的核心JSBridge
的实现
由于在最新版本中,已经没有考虑iOS7
等低版本,因此在选用方案时没有采用url scheme
方式,而是直接基于WKWebView
实现
交互原理
具体H5和Native的交互原理可以参考前文的H5和Native交互原理
交互原理图如下:
预计的最终效果
如果一步一步来分析,最后再看效果,可能会很枯燥,甚至还有点化简为繁的样子。(感觉直接看代码应该是最简单的,奈何每次写成文章时都得加一大堆的描述)
因此,先来看看最终完成后应该是什么样的。
// 调用ui中alert的示例
callHandler({// 模块名,本文中的API划分了模块module: 'ui',// 方法名name: 'alert',// 需要传递给native的请求参数data: {message: 'hello',},callback: function(res) {/*** 调用后的回调,接收原生传递的回调数据* alert如果成功,可以点击后再回调{// 1成功/0失败code: 1,message: '描述',// 数据data: {},}*/}
});
架构
从头开始实现一个JSBridge,很容易两眼一抹黑,无从下手。
因此我们需要先从大方向上把功能交互确定好,然后再开始构建细节,编码实现
功能分析与确认
根据核心架构,规划需要实现的功能:
H5桥接对象的设计(JSBridge)
短期回调池,需自动回收
长期回调池,可多次使用
调用Native方法的通道,桥接对象上原生注册的接收方法
接收Native调用的通道,桥接对象上H5注册的接收方法
H5可以注册主动给原生调用的方法
原生桥接对象的设计
长期方法池,每一个长期调用都会存储在回调池中,可以多次使用
短期立即执行,每一个短期调用都是立即执行
调用H5方法的通道,桥接对象上H5注册的接收方法
接收H5调用的通道,桥接对象上原生注册的接收方法,底层自动解析,然后执行对应API
回调对象,底层基于调用H5的通道,每次执行完毕后都通过回调对象回调给H5
主动调用H5,不同于回调对象只能被动响应,这个可以主动调用H5中注册的方法
API的设计
H5中的API,供前端调用,底层通过调用Native方法的通道,然后将预处理后的参数发送给原生
Native中的API,真正的功能实现
接下来就是JSBridge的实现
全局通信对象的确认
最重要的,是先把H5和Native通信时的几个全局桥接对象确定:
JSBridge
,H5端的桥接对象,对象中绑定了接收原生调用的方法_handleMessageFromNative
,以及内部有对回调函数等进行管理webkit.messageHandlers.WKWebViewJavascriptBridge.postMessage
,iOS端的桥接对象,这个方法接收H5的调用prompt
,Android端的桥接对象,为了方便,直接重写了WebChromeClient
中的onJsPrompt
// H5端的内部逻辑处理
window.JSBridge = {...}// 接收原生的调用,有回调以及主动调用两种
JSBridge._handleMessageFromNative = function() {...}
// H5主动调用原生
if (os.ios) {// ios采用window.webkit.messageHandlers.WKWebViewJavascriptBridge.postMessage(...);
} else {window.top.prompt(...);
}
JSBridge对象的实现
H5就依靠这个对象与Native通信,这里仅介绍核心的逻辑
JSBridge = {// 本地注册的方法集合,原生只能主动调用本地注册的方法messageHandlers: {},// 短期回调函数集合,在原生调用完对应的方法后会自动删除回收responseCallbacks: {},// 长期存在的回调集合,可以多次调用responseCallbacksLongTerm: {},_handleMessageFromNative: function(messageJSON) {// 内部的处理:/**如果是回调函数:如果是短期回调responseCallbacks中查询回调id,并执行,执行后自动销毁如果是短期回调responseCallbacksLongTerm中查询回调id,并执行*//**如果是Native的主动调用:去本地注册的方法池messageHandlers中搜索,并执行*/},callHandler: function(...) {// 底层分别调用Android或iOS的原生接收方法// 如果是短期回调,会将回调添加到responseCallbacks中// 如果是长期回调,会将回调添加到responseCallbacksLongTerm中// 省略若干逻辑...if (os.ios) {// ios采用window.webkit.messageHandlers.WKWebViewJavascriptBridge.postMessage(...);} else {window.top.prompt(...);}},registerHandler: function(handlerName, handler) {// H5在本地注册可供原生调用的方法},...
};
Android中桥接对象的实现
Android中的核心就是JSBridge
,其余都是围绕这个来的,以下是伪代码,列举主要的逻辑
public class JSBridge {// 缓存所有的API模块(注册时添加进去)static exposedAPIModlues = new HashMap<>();static register(String apiModelName, Class<? extends IBridgeImpl> clazz) {// 注册时会自动寻找所有的框架API模块,然后添加到缓存exposedAPIModlues,每一个模块中可以有若干API// 每一个模块都需要实现IBridgeImpl接口...}static callAPI(...) {// 首先会解析参数(H5中传递的),解析出调用了哪一个API,传递了些什么,解析结果包括如下// port:H5传递的回调id,是responseCallbacks或responseCallbacksLongTerm中的key// moduleName:调用的API的模块名,用来检索exposedAPIModlues中注册的模块// name:调用的API的方法名,在对于找到的模块中去查找API// 其他:包括传递的参数等等// 然后会根据H5的回调端口号,生成一个回调对象(用来回调通知H5)Callback callback = new Callback(port);// 之后,根据解析的参数寻找API方法// java.lang.reflect.Method;Method method = searchMethodBy(moduleName, name);// 没有找到方法会回调对于错误信息// 否则执行对于的method,传递解析出的参数// 并且在method内部执行完毕后主动回调给H5对于信息method.invoke(..., callback);}
}
callback
类伪代码如下:
public class Callback {apply(...) {// 先解析拼装参数,然后将参数组装成javascript代码,参数中包含Callback对于的port值(回调id)...String js = javascript:JSBridge._handleMessageFromNative(对于的json参数);callJS(js);}callHandler(...) {// 主动调用H5,封装的参数中不再是回调id,而是handleName...callJS(js);}callJS(js) {// 底层通过loadUrl执行...webviewContext.loadUrl(js);}
}
IBridgeImpl
接口是空的,只是一个抽象定义,以下以某个实现这个接口的API为例
// 为了清晰,以ui.alert为例
public class xxxApi implements IBridgeImpl {// 定义一个注册的模块别名,方便查找,譬如uistatic RegisterName = "ui";// 模块中的某个API,譬如alertpublic static void alert(..., Callback callback) {// 接下来就是在这个API中实现对于的逻辑...// 最后,通过触发callback通知H5即可callback.apply(...);}
}
最后可以看到,在webview
中,重新了WebChromeClient
的onJsPrompt
来接收H5的调用
并且在webview
加载时就会调用JSBridge
的register
public class XXXWebChromeClient extends WebChromeClient {@Overridepublic boolean onJsPrompt(..., JsPromptResult result) {// 内部触发JSBridge.callJavaresult.confirm(JSBridge.callJava(...));return true;}
}
以上几个就是Andorid
中JSBridge核心实现,其他的如长期回调,短期回调,细节实现等优化不是核心逻辑,就列举,详情可以参考最后的源码
iOS中桥接对象的实现
这里仍然是OC
实现的,主要参考的marcuswestin/WebViewJavascriptBridge实现
核心仍然是WKWebViewJavascriptBridge
,其余一切都是通过它来分发代理
@implementation WKWebViewJavascriptBridge {// 内部基于一个WebViewJavascriptBridgeBase基类(基类中定义交互方法)WebViewJavascriptBridgeBase *_base;
}
/*** API*/
- (void)callHandler:(NSString *)handlerName data:(id)data {// 主动调用H5的方法// 底层调用_base的sendData,发送数据给H5
}- (void)registerModuleFrameAPI {// 注册模块API,模块用到了别名代理[self registerHandlersWithClassName:@"UIApi" moduleName:@"ui"];// 其中registerHandlersWithClassName就是将模块示例化注册到全局中的作用,不赘述
}- (void)excuteMessage:(NSString *)message {// 内部执行API的实现,这里会解析API解析出来的数据,如// module.name,port(callbackid)等...// 然后底层调用_base的excuteMsg(它内部会根据注册的API,找到相对应的,然后执行原生功能,最后通过回调通知H5)
}#pragma mark - WKScriptMessageHandler其实就是一个遵循的协议,它能让网页通过JS把消息发送给OC
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {// 监听到对于API调用时,底层会调用excuteMessageif ([message.name isEqualToString:@"WKWebViewJavascriptBridge"]) {[self excuteMessage:message.body];}
}
然后看看它基类WebViewJavascriptBridgeBase
的实现
@implementation WebViewJavascriptBridgeBase- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {// 底层将接收到的数据组装成js代码执行...NSString* javascriptCommand = [NSString stringWithFormat:@"JSBridge._handleMessageFromNative('%@');", messageJSON];[_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
}- (void)excuteMsg:(NSString *)messageQueueString moduleName:(NSString *)moduleName {// 底层根据对于的模块,API名,找到注册的handler...// 然后创建一个回调对象WVJBResponseCallback responseCallback = (通过sendData通知H5回调数据);// 然后执行这个handlerhandler(message[@"data"], responseCallback);
}
接下来是API的定义
定义API模块之前,需要先了解RegisterBaseClass
,所有模块必须实现的基类,定义了如何注册
@implementation RegisterBaseClass
#pragma mark - 注册api的统一方法
- (void)registerHandlers {// 子类重写改方法实现自定义API注册
}#pragma mark - handler存取
- (void)registerHandlerName:(NSString *)handleNamehandler:(WVJBHandler)handler {// 注册某个模块下的某个API
}- (WVJBHandler)handler:(NSString *)handlerName {// 通过名称获取对应的API
}
要定义一个API模块,则需继承RegisterBaseClass
然后重写registerHandlers
(为了清晰,以ui.alert为例)
@implementation UIApi
- (void)registerHandlers {[self registerHandlerName:@"alert" handler:^(id data, WVJBResponseCallback responseCallback) {// 同样,在接收到数据,并处理后,通过responseCallback通知H5...responseCallback(...);}
}
在webview
加载时就会调用WKWebViewJavascriptBridge
的registerModuleFrameAPI
,对于模块名ui
与别名UIApi
,可以在注册时看到,它们之间是有一一对应关系的
然后在webview创建时,会进行监听,userContentController
WKWebViewConfiguration * webConfig = [[WKWebViewConfiguration alloc] init];
WKUserContentController * userContentVC = [[WKUserContentController alloc] init];
webConfig.userContentController = userContentVC;
WKWebView * wk = [[WKWebView alloc] initWithFrame: CGRectZero configuration: webConfig];self.wv = wk;
...// 代理
self.bridge = [WKWebViewJavascriptBridge bridgeForWebView: self.wv];
[self.bridge setWebViewDelegate: self];// 添加供js调用oc的桥梁。这里的name对应WKScriptMessage中的name,多数情况下我们认为它就是方法名。
[self.wv.configuration.userContentController addScriptMessageHandler: self.bridge name: @"WKWebViewJavascriptBridge"];
同样,iOS中的长期回调等其它一些非核心内容也暂时隐藏了
API的设计
按照上述的实现,可以构建出一个完整的JSBridge交互流程,H5和Native的交互已经通了
接下来就是设计API真正给外界调用
准确的来说,API的设计已经脱离了JSBridge交互内容,属于混合框架框架应用层次,因此后续会有单独的章节介绍quick hybrid
中的API
API如何实现?可以参考上文中Android的继承IBridgeImpl
法以及iOS的继承RegisterBaseClass
然后重写registerHandlers
至于该规划些什么API,这与实际的需求有关,不过一般情况下,像ui.alert
等等一般都是必须的
更多详情请待后续章节
结束语
最后再来一张图巩固下把
至此,整个JSBridge交互就已经完成了
其实在总结文章时,考虑过很多种形式,发现,
如果是全文字描述,十分枯燥,很难坚持读下来,
如果是各种原理都用绘图+描述,发现会化简为繁,硬生生把难度提高了几个level,
所以最终采用的是伪代码(半伪半真)展示形式(剔除一些无效信息,提取关键,而且还不和最终的代码冲突)
虽然说,这整套流程都没有特别难的地方,涉及的知识点都不是特别深。但是却包含了前端,Android,iOS三个领域。
因此如果要将整套工作做的比较好的化最好还是有分工的好,比较一个人的精力有限,真正专精多个领域的人还是比较少的,
而且后续各个优化的内容也不少(API,优化,等等...)
返回根目录
- 【quickhybrid】如何实现一个Hybrid框架
源码
github
上这个框架的实现
quickhybrid/quickhybrid
附录
参考资料
- marcuswestin/WebViewJavascriptBridge
转载于:https://www.cnblogs.com/dailc/p/8098597.html
【quickhybrid】JSBridge的实现相关推荐
- 【quickhybrid】组件(自定义)API的实现
前言 前文在API规划时就已经有提到过组件API这个概念,本文将会介绍它的原理以及实现 理解组件API这个概念 quick.ui.xxx quick.page.xxx 复制代码 在quick hybr ...
- 【quickhybrid】架构一个Hybrid框架
前言 虽然说本系列中架构篇是第一章,但实际过程中是在慢慢演化的第二版中才有这个概念, 经过不断的迭代,演化才逐步稳定 明确目标 首先明确需要做成一个什么样的框架? 大致就是: 一套API规范(统一An ...
- 【quickhybrid】H5和Native交互原理
前言 Hybrid架构的核心就是JSBridge交互,而实现这个交互的前提是弄清楚H5和Native端的交互 本文主要介绍Native端(Android/iOS)和H5端(泛指前端)的交互原理 (之前 ...
- 【quickhybrid】API的分类:短期API、长期API
前言 一切就绪,开始规划API,这里在规划前对API进行了一次分类:__短期API.长期API__ 首先申明下,这个是在实际框架演变过程中自创的一个概念,其它混合框架可能也会有这个概念,但应该是会在原 ...
- 【quickhybrid】H5和原生的职责划分
前言 在JSBridge实现后,前端网页与原生的交互已经通了,接下来就要开始规划API,明确需要提供哪一些功能来供前端调用. 但是在这之前,还有一点重要工作需要做: 明确H5与Native的职责划分, ...
- JSBridge 技术原理分析
- JSBridge的起源 - PhoneGap(Codova 的前身)作为 Hybrid 鼻祖框架,是一个开源的移动开发框架,允许你用标准的web技术-HTML5,CSS3和JavaS ...
- 为自己搭建一个鹊桥 -- Native Page与Web View之间的JSBridge实现方式
原文:为自己搭建一个鹊桥 -- Native Page与Web View之间的JSBridge实现方式 说起JSBridge,大家最熟悉的应该就是微信的WeixinJSBridge,通过它各个公众页面 ...
- Android之利用JSBridge库实现Html,JavaScript与Android的所有交互
java 和 js互通框架 WebViewJavascriptBridge是移动UIView和Html交互通信的桥梁,用作者的话来说就是实现java和js的互相调用的桥梁. 替代了WebView的自带 ...
- JSBridge深度剖析
概述 做过混合开发的人都知道Ionic和PhoneGap之类的框架,这些框架在web基础上包装一层Native,然后通过Bridge技术的js调用本地的库. 在讲JSBridge技术之前,我们来看一下 ...
最新文章
- 拇指接龙游戏升级记录03(升级MainScene.cpp)
- SAP MM Transportation of PR Release Strategy with Classification
- python to_excel保存成xls_pd.ExcelWriter(to_excel)保存结果到已存在的excel文件中
- oracle日志分析产品,鼎甲技术应用:Oracle日志分析 之事务级精准恢复
- Bing改善Microsoft Office及Edge浏览器图像搜索功能
- Vue和React组件之间的传值方式
- linux shell并发执行命令
- 在Linux中,用.swp文件恢复未保存的文件
- animals中文谐音_魔力红歌曲Aanimals中文谐音,就是音译歌词
- Inno Setup选择语言并写入配置文件、翻译提示信息
- 笔记本电脑硬盘不见了_笔记本电脑找不到硬盘原因及解决方法
- 发json请求,报415错误。POST http://localhost:8080/ 415
- 电机的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- 若依框架,集成flowable工作流
- apple tv 开发_您应该从Apple TV 3升级到Apple TV 4还是4K?
- 游戏项目管理经验方法
- 图片映射字体反爬-某租房网
- VMware Workstation 虚拟机安装
- 志愿填报在即,店宝宝告诉你,高薪的专业有哪些?
- 弹性伸缩定时任务支持Cron表达式 1
热门文章
- mysql .pdb是什么文件_计算广告算法到底要做什么?
- php输出json到表格,PHP中把数据库查询结果输出为json格式
- mysql导入创建表空间_oracle创建表空间 用户 数据库导入和导出(转)
- python学生类出不来中文_Python 这类看起来学习门槛低的语言,是否真的适合入门编程学习?...
- python脚本:判断字符是否为中文
- c# 中关键字_C#中的“使用”关键字
- 超图软件试用许可操作步骤_软件中的操作步骤
- Java InputStreamReader getEncoding()方法及示例
- 03-对图像进行数值计算和加权融合
- 搭建srs服务器(rtmp)