众所周知,app的一些功能可能会使用到H5开发,这就难免会遇到java与js的相互调用,像android利用WebViewJavascriptBridge实现js和java的交互,这里主要介绍下JsBridge的原理和使用。
![626157c2107a4d9c788ef1164799c5ce.png](https://i-blog.csdnimg.cn/blog_migrate/1daeaeafc4e3d8705292beb2f13d79e0.jpeg)
一、什么是JSBridge
JSBridge主要是给JavaScript提供调用Native功能的接口,让混合开发中的前端部分可以方便地使用Native的功能(例如:地址位置、摄像头)。而且JSBridge的功能不止调用Native功能这么简单宽泛。
JSBridge是一座用JavaScript搭建起来的桥,一端是Web,一端是Native,是Native和Web之间的桥梁。我们搭建这座桥的目的也很简单,让Native可以调用Web的js代码,让Web可以“调用”原生的代码。它的核心是构建Native和Web间消息通信的通道,而且这个通信的通道是双向的。
双向通信的通道:JS向Native发送消息:调用相关功能、通知Native当前JS的相关状态等。Native向JS发送消息:回溯调用结果、消息推送、通知JS当前Native的状态等。
H5与Native交互如下图:
![cf4b03d66ef104bb5c03bb65404cc9fa.png](https://i-blog.csdnimg.cn/blog_migrate/dcb4d876472b5410125c71e238822bae.jpeg)
二、JSBridge的实现原理
JavaScript是运行在一个单独的JS Context中(例如WebView的Webkit引擎、JSCore)。由于这些Context与原生运行环境的天然隔离,我们可以将这种情况与RPC(Remote Procedure Call,远程过程调用)通信进行类比,将Native与JavaScript的每次互相调用看做一次RPC调用。
在JSBridge的设计中,可以把前端看做RPC的客户端,把Native端看做RPC的服务器端,从而JSBridge要实现的主要逻辑就出现了:通信调用(Native与JS通信)和句柄解析调用。
![7b40f33f265b462329ad0bf0dfd95098.png](https://i-blog.csdnimg.cn/blog_migrate/86778b2c7ad1ffd2a1308088f45f726c.jpeg)
三、JSBridge的通信原理
1.JavaScript调用Native的方式
主要有两种:注入API和拦截URL SCHEME
1.1 注入API
注入API方式的主要原理是,通过WebView提供的接口,向JavaScript的Context(window)中注入对象或者方法,让JavaScript调用时,直接执行相应的Native代码逻辑,达到JavaScript调用Native的目的。
iOS
UIWebVIew(iOS2+)和WKWebView(iOS8+)的调用方式有所区别
//假设ios客户端约定方法名为nativeBridge//UIWebViewwindow.nativeBridge(message);//WKWebViewwindow.webkit.messageHandlers.nativeBridge.postMessage(message);
Android
原理:通过WebView提供的addJavascriptInterface方法给浏览器window注入一个命名空间,然后给Web增加一些可以操作Java的反射。
//addJavascriptInterfacemWebView.addJavascriptInterface(new Class(), 'android'); //@JavascriptInterfacepublic class Class(){ @JavascriptInterface public void method(){ }}//js 代码window.android.method();
备注:在4.2之前,Android注入JavaScript对象的接口是addJavascriptInterface,但是这个接口有漏洞,可以被不法分子利用,危害用户的安全,因此在4.2中引入新的接口@JavascriptInterface(上面代码中使用的)来替代这个接口,解决安全问题。
1.2 拦截URL SCHEME
解释一下URL SCHEME:URL SCHEME是一种类似于url的链接,是为了方便app直接互相调用设计的,形式和普通的url近似,主要区别是protocol和host一般是自定义的。
例如:esign://home/url?url=www.baidu.com,protocol是esign,host则是home。
拦截URL SCHEME 的主要流程是:Web端通过某种方式(例如iframe.src)发送URLScheme请求,之后Native拦截到请求并根据URL SCHEME(包括所带的参数)进行相关操作。
在时间过程中,这种方式有一定的缺陷:
- 使用iframe.src发送URLSCHEME会有url长度的隐患。
为什么选择iframe.src不选择locaiton.href?因为如果通过location.href连续调用Native,很容易丢失一些调用。
创建请求,需要一定的耗时,比注入API的方式调用同样的功能,耗时会较长。
因此:JavaScript调用Native推荐使用注入API的方式
![173af1a1b097c5941fccc5d6e8dd32a7.png](https://i-blog.csdnimg.cn/blog_migrate/3496f740fed869606259625b0aca1207.jpeg)
2.Native调用JavaScript的方式
相比于JavaScript调用Native,Native调用JavaScript较为简单,直接执行拼接好的JavaScript代码即可。
从外部调用JavaScript中的方法,因此JavaScript的方法必须在全局的window上。
iOS
对于iOS的UIWebView,示例如下:
//UIWebViewresult = [uiWebview stringByEvaluatingJavaScriptFromString:javaScriptString];
对于iOS的WKWebView,示例如下:
//WKWebView[wkWebView evaluateJavaScript:javaScriptString completionHandler:completionHandler];
Android
Android
在Kitkat(4.4)之前是使用webview的loadUrl进行调用的:
webView.loadUrl("javascript:JSBridge.trigger('webviewReady')");
而Kitkat之后的版本,也可以用evaluateJavascript方法实现:
webView.evaluateJavascript(javaScriptString,new ValueCallback() { @Override publicvoidonReceiveValue(String value){ }});
![7fe406408445401d3b254cae9aecf703.png](https://i-blog.csdnimg.cn/blog_migrate/a16f60fbe52fad654f6bfb0759772473.jpeg)
四、JSBridge 接口实现
从上面的剖析中,可以得知,JSBridge的接口主要功能有两个:调用Native(给Native发消息)和接被Native调用(接收Native消息)。因此,JSBridge可以设计如下:
window.JSBridge = { // 调用 Native invoke: function(msg) { // 判断环境,获取不同的 nativeBridge nativeBridge.postMessage(msg); }, receiveMessage: function(msg) { // 处理 msg }};
在上面部分中,提到过RPC中有一个非常重要的环节是句柄解析调用,这点在JSBridge中体现为句柄与功能对应关系。同时,我们将句柄抽象为桥名(BridgeName),最终演化为一个BridgeName对应一个Native功能或者一类Native消息。基于此点,JSBridge的实现可以优化为如下:
window.JSBridge = { // 调用 Native invoke: function(bridgeName, data) { // 判断环境,获取不同的 nativeBridge nativeBridge.postMessage({ bridgeName: bridgeName, data: data || {} }); }, receiveMessage: function(msg) { var bridgeName = msg.bridgeName, data = msg.data || {}; //具体逻辑 }};
终极提问:消息都是单向的,那么调用Native功能时Callback怎么实现的?
对于JSBridge的Callback,其实就是RPC框架的回调机制。当然也可以用更简单的JSONP机制解释:
当发送JSONP请求时,url参数里会有callback参数,其值是当前页面唯一的,而同时以此参数值为key将回调函数存到window上,随后,服务器返回script中,也会以此参数值作为句柄,调用相应的回调函数。
整体流程:
在Native端配合实现JSBridge的JavaScript调用Native逻辑也很简单,主要的代码逻辑是:接收到JavaScript消息=>解析参数,拿到bridgeName、data和callbackId=>根据bridgeName找到功能方法,以data为参数执行=>执行返回值和callbackId一起回传前端。
Native调用JavaScript也同样简单,直接自动生成一个唯一的ResponseId,并存储句柄,然后和data一起发送给前端即可。
![7285bb214990f718327f0940f1c8a582.png](https://i-blog.csdnimg.cn/blog_migrate/fab12784bd0a2cebb86b2e06c41a3637.jpeg)
五、JSBridge的总结
对于JSBridge的引用,常用有如下两种方式,但各有利弊。
1.由Native端进行注入
注入方式和Native调用JavaScript类似,直接执行桥的全部代码。
它的优点是:
桥的版本很容易与Native保持一致,Native端不用对不同版本的JSBridge进行兼容。
它的缺点是:
注入时机不确定,需要实现注入失败后重试的机制,保证注入的成功率,同时JavaScript端在调用接口时,需要优先判断JSBridge是否已经注入成功。
2.由JavaScript端引用
直接与JavaScript一起执行。
它的优点是:
JavaScript端可以确定JSBridge的存在,直接调用即可。
它的缺点是:
如果桥的实现方式有更改,JSBridge需要兼容多版本的Native Bridge或者Native Bridge兼容多版本的JSBridge。
至此,JSBridge的原理介绍完毕,欢迎大家转发留言进行交流。