JSbridge原理与实现简析
JSBridge
定义
- 定义:正如其命名一样,
JSBridge
就相当于js
与native
之间进行全双工通信的一座桥梁,其内部定义了一套用于js
与native
进行通信的规范(包括协议、方法、传参及回调等); - 用途:
JSBridge
可以桥连js
与native
的通信,从而使基于容器的web
开发和优化成为可能,如比较火的hybrid app
技术;能够提升页面性能,丰富页面功能等;
JSBridge
原理简析6+
框架简析:
JSBridge
框架其实主要由两部分组成:第一部分是Native
调用js
,主要用于消息推送、状态同步及回溯调用结果等;第二部分是js
调用Native
,主要用于调用系统api、事件监听及状态同步等;Native
调用js
- Android
Android
有两种方式:4.4.0
以前使用方法loadUrl
——调用方便,无法获取回调结果,会刷新webview;4.4.0+
使用方法evaluateScript
提供更加高效完善的功能——可以获取返回值并且不刷新webview;
需要注意的是,这里可调用的方法是全局方法;# loadUrl mWebView.loadUrl("javascript: methodName(paramStr)");# evaluateScript mWebView.evaluateScript("javascript: method(paramStr)", new ValueCallback<String>() {@overridepublic void onReceiveValue() {// do something after receive js callback value} }
- IOS
# UIWebView mWebView.stringByEvaluatingJavaScriptFromString("methodName(paramStr)");#WKWebView mWebView.evaluateScript("methodName(paramStr)");
- Android
js
调用Native
url schema
拦截
h5
和native
约定一套通信协议作为通信基础,一般如下:
schema://methodName?params=xxx&cb=xxx
;
其中schema
为双方协商的协议名,methodName
为js调用native的方法名,params
为参数集字符串,cb
为接收回调结果的js
方法名;在h5
中发起请求时,一般通过构建一个不可见的iframe
发起请求;请求以约定的方式以url
形式发送,native
会拦截h5
的所有请求(如进行长连接优化等),如果发现url
中的协议名是约定的协议名(如jsbridge),则会解析其中的methodName
、params
及cb
等信息。如下给出了简单实现:window.callId = 0; const callNative = (method, params = null, cb) => {const paramsObj = {data: params ? JSON.stringify(params) : null,}if (typeof cb === 'function') {const cbName = cb + window.callId++;window[cbName] = cb;paramsObj['cbName'] = cbName;}// 设定通信url供native拦截const url = `jsbridge://${method}?${JSON.stringify(paramsObj)}`;const iframe = document.createElement('iframe');iframe.src = url;iframe.style.width = 0;iframe.style.height = 0;iframe.frameborder = 0;iframe.style.display = 'none';document.body.appendChild(iframe);setTimeout(() => {iframe.parentNode.removeChild(iframe);}, 100); }
缺点:消息传输通过
url
传输,因此传输数据长度受到限制;prompt
、alert
、confirm
拦截
一般通过prompt
进行通信,其他实现与url schema
拦截类似;native
收到prompt
事件后会通过onJsPrompt
等类似事件对prompt
做处理,从而获取js
传入的method
、params
、cb
等;function callNative(method, params, cb) {...const url = `jsbridge://${method}?${JSON.stringify(paramsObj)}`;prompt(url); }
缺点:
ios
的UIWebkit
不支持;注入JS上下文
这种方法一般是将需要供js
调用的native
方法通过实例对象的方式通过webview提供的方法注入到js全局上下文,这样位于webview内的h5
页面中js
可以直接调用native
的实例方法;
注入方式:Android的webview通过addJ avascriptInterface
;ios UIWebview
通过JSContext
;ios WKWebview
通过scriptMessageHandler
;
下面是来自文献2的Android客户端关于注入JS
上下文的简单demo;首先,声明一个
NativeMethods
的类,用于定义对外暴露给js
调用的方法,格式如methodName(webview, args, cb)
;
然后定义一个JSBridge
类:其中定义了两个静态方法和一个实例方法;register
用于将nativeMethods
类下的方法转为hashMap格式,便于查询;call
是暴露给js
的供js
统一调用的方法;
最后在webview创建后注册对外方法并将JSBridge
实例通过addJavascriptInterface
注入到JS全局上下文中;public class MainActivity extends AppCompatActivity {private WebView mWebView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView = (WebView) findViewById(R.id.mWebView);...// 将 NativeMethods 类下面的提供给 js 的方法转换成 hashMapJSBridge.register("JSBridge", NativeMethods.class);// 将 JSBridge 的实例对象注入到 js 全局上下文中,名字为 _jsbridge,该实例对象下有 call 方法mWebView.addJavascriptInterface(new JSBridge(mWebView), "_jsBridge");} }public class NativeMethods {// 用来供 js 调用的方法public static void methodName(WebView view, JSONObject arg, CallBack callBack) {} }public class JSBridge {private WebView mWebView;public JSBridge(WebView webView) {this.mWebView = webView;}private static Map<String, HashMap<String, Method>> exposeMethods = new HashMap<>();// 静态方法,用于将传入的第二个参数的类下面用于提供给 javacript 的接口转成 Map,名字为第一个参数public static void register(String exposeName, Class<?> classz) {...if (!exposeMethods.containsKey(exposeName)) {exposeMethods.put(exposeName, getAllMethod(classz));}}// 实例方法,用于提供给 js 统一调用的方法@JavascriptInterfacepublic String call(String methodName, String args) {...} }
根据上述代码可以看到注入到
js
全局对象中的实例对象为_jsBridge
;在h5
中的调用如下:window.callId = 0; const callNative = (method, params = null, cb) => {const paramsObj = {data: params ? JSON.stringify(params) : null,}if (typeof cb === 'function') {const cbName = cb + window.callId++;window[cbName] = cb;paramsObj['cbName'] = cbName;}if (window._jsBridge) {window._jsBridge.call(method, JSON.stringify(paramsObj));} else {// 兜底方案const url = `jsbridge://${method}?${JSON.stringify(paramsObj)}`;prompt(url);}
缺点:安卓4.2以下存在安全漏洞,可能会导致用户信息泄露;
JSBridge
静态方法的call
方法实现:public static String call(WebView webView, String urlString) {if (!urlString.equals("") && urlString!=null && urlString.startsWith("jsbridge")) {Uri uri = Uri.parse(urlString);String methodName = uri.getHost();try {JSONObject args = new JSONObject(uri.getQuery());JSONObject arg = new JSONObject(args.getString("data"));String cbName = args.getString("cbName");if (exposeMethods.containsKey("JSBridge")) {HashMap<String, Method> methodHashMap = exposeMethods.get("JSBridge");if (methodHashMap!=null && methodHashMap.size()!=0 && methodHashMap.containsKey(methodName)) {Method method = methodHashMap.get(methodName);if (method!=null) {method.invoke(null, webView, arg, new CallBack(webView, cbName));}}}} catch (Exception e) {e.printStackTrace();}}return null; }
除此之外,
js
调用native
方法成功需要给h5
响应的结果反馈,因此native
端需要定义一个Callback
类用于处理js
调用成功的结果反馈;其本质还是native
调用js
方法,以下是安卓端使用evaluatecript
方法实现的Callback
类:public class CallBack {private String cbName;private WebView mWebView;public CallBack(WebView webView, String cbName) {this.cbName = cbName;this.mWebView = webView;}public void apply(JSONObject jsonObject) {if (mWebView!=null) {mWebView.post(() -> {mWebView.evaluateJavascript("javascript:" + cbName + "(" + jsonObject.toString() + ")", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {return;}});});}} }
JSBridge的引用
- 由
h5
引用
由h5
引用即使用对应的封装好的JSBridge
的npm
包,内部封装了js
调用native
方法的方法集;该方式能够保证调用时JSBridge
一定存在;缺点是当有变更时,需要native
与h5
同时做变更进行兼容; - 由
native
注入
即上边提到的将JSBridge
实例对象注入到js
全局上下文;该方式有利于保障API与Native的一致性,但是由于注入方法和时机受到限制,h5
调用时总是要判断JSBridge
实例对象是否存在;
参考文献
- JSBridge 深度剖析
- 从零开始写一个 JSBridge
JSbridge原理与实现简析相关推荐
- ntp如何确认与服务器偏差_NTP时钟同步原理及误差简析
原标题:NTP时钟同步原理及误差简析 在我们某试验系统方案设计中,由于数据同步性的要求,需要将我们WP4000变频功率分析仪的时钟与客户的NI系统的时钟进行同步,对于WP4000变频功率测试系统而言, ...
- NTP时钟服务器原理及误差简析(京准)
NTP时钟服务器原理及误差简析 1.引言 作为数字通信网的基础支撑技术,时钟同步技术的发展演进始终受到通信网技术发展的驱动.在网络方面,通信网从模拟发展到数字,从TDM网络为主发展到以分组网络为主:在 ...
- NTP时钟同步原理及误差简析
在我们某试验系统方案设计中,由于数据同步性的要求,需要将我们WP4000变频功率分析仪的时钟与客户的NI系统的时钟进行同步,对于WP4000变频功率测试系统而言,多台分析仪之间可通过同步光纤接口达到严 ...
- mmap内存 android,Android中mmap原理及应用简析
mmap是Linux中常用的系统调用API,用途广泛,Android中也有不少地方用到,比如匿名共享内存,Binder机制等.本文简单记录下Android中mmap调用流程及原理.mmap函数原型如下 ...
- SIFT特征原理简析(HELU版)
SIFT(Scale-Invariant Feature Transform)是一种具有尺度不变性和光照不变性的特征描述子,也同时是一套特征提取的理论,首次由D. G. Lowe于2004年以< ...
- ceph存储原理_Ceph存储引擎BlueStore简析
前文我们创建了一个单节点的Ceph集群,并且创建了2个基于BlueStore的OSD.同时,为了便于学习,这两个OSD分别基于不同的布局,也就是一个OSD是基于3中不同的存储介质(这里是模拟的,并非真 ...
- Webpack模块化原理简析
webpack模块化原理简析 1.webpack的核心原理 一切皆模块:在webpack中,css,html.js,静态资源文件等都可以视作模块:便于管理,利于重复利用: 按需加载:进行代码分割,实现 ...
- Android Handler与Looper原理简析
一直感觉自己简直就是一个弱智,最近越来越感觉是这样了,真的希望自己有一天能够认同自己,认同自己. 本文转载于:https://juejin.im/post/59083d7fda2f60005d14ef ...
- 基于IdentityServer4的OIDC实现单点登录(SSO)原理简析
# 写在前面 IdentityServer4的学习断断续续,兜兜转转,走了不少弯路,也花了不少时间.可能是因为没有阅读源码,也没有特别系统的学习资料,相关文章很多园子里的大佬都有涉及,有系列文章 ...
- grpc通信原理_gRPC原理简析
gRPC原理简析 gRPC是由谷歌提出并开发的RPC协议,gRPC提供了一套机制,使得应用程序之间可以进行通信. 降级开发者的使用门槛,屏蔽网络协议,调用对端的接口就像是调用本地的函数一样.而gRPC ...
最新文章
- BE镜像还原系统过程
- 服务器硬件电路设计书籍,家庭网关硬件接口电路设计大全——电路精选(3)...
- WPF数据绑定、多个元素
- 计算机与应用教学,教学方法与教学手段
- MATLAB-M文件
- JSP EL 表达式取request parameter
- [设计模式]代理模式
- ios 顶部tab滑动实现_iOS开发之多表视图滑动切换示例(仿头条客户端)
- 【nyoj - 890】 分东西 (水题 二进制)
- java中字母用什么单词赋值_Java初学
- M斐波那契数列(HDU-4549)
- 常用CSS优化总结——网络性能与语法性能建议
- JavaScript 函数 对象 数组
- 神经网络ANN分类器及OpenCV实现
- 激光技术领域的又一重大突破:光学频率梳
- android关于自定义Dialog中布局match_parent 属性 失效的问题
- phalapi可以依赖注入么_PhalApi 2.7 开发快速上手
- 用python画钢铁侠图片_Photoshop快速把钢铁侠图片转为素描水墨风格教程
- 回归预测分析(RANSAC、多项式回归、残差图、随机森林)
- 点石互动--kyw之:30步,网站信任度提升200%