随着HTML5的不断普及及优化,以及移动端对动态化的需求越来越大,开发者经常需要在app中嵌入一些网页,然后会在web和native之间进行交互,如传递数据,调用函数,而连接web与native需要一个桥梁。本文主要对比github新秀DSBridge与经典的WebViewJavascriptBridge各方面对比。
下面是DSBridge和WebViewJavascriptBridge的github地址:
DSBridge-IOS:github.com/wendux/DSBr…
DSBridge-Android:github.com/wendux/DSBr…
WebViewJavascriptBridge(ios) github.com/marcuswesti…
约定:一下术语“端”指的是native(ios/android), 而 “前端” 特指 web端(javascript);
通过github上的信息可以看到DSBridge官方是同时支持ios/android的,但是注意到,DSBridge ios版是支持wkwebview的,而wkwebview也是可以用在osx中,也就是说DSBridge ios版也是可以用于mac开发的。也就是说DSBridge同时支持:ios/android/osx。再来看看WebViewJavascriptBridge,官方说明是支持ios/osx的,但WebViewJavascriptBridge并不支持android, 当然,由于WebViewJavascriptBridge的人气实在太高,也有一些人在android上实现了兼容的版本如这个点我,但是总的来说,并非一家之作,这可能会给日后维护带来问题。而DSBridge是同一个作者,如果将来更新时可以保证双端同步。第一回合,DSBridge胜!
Javascript Bridge比较特殊,因为它要同时涉及三端,让三个端使用起来都比较容易是很有挑战的一个任务,这不仅是能力层面,也和接口和使用方式的设计息息相关,当然能在三端之间做出最好的平衡,也对作者能力要求比较高(必须同时了解ios/android/web)。 接下来我们分别从三端的接口和使用方式做一个详细对比。
Javascript调用native
假设native有个回显信息的函数,签名如下:
String echo(JSONObject args); //先用java描述
该函数功能是js调用时传递一个字符串msg给端,然后端上再返回 “you put string “+msg; 参数以json传递。
先来看看WebViewJavascriptBridge的调用方式:
第一步:复制 setupWebViewJavascriptBridge 函数声明到你的js中:
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
第二步:调用 setupWebViewJavascriptBridge,在回调中获得bridge, 然后通过bridge调用native方法:
setupWebViewJavascriptBridge(function(bridge) {
bridge.callHandler('echo', {'msg':'hello world'}, function responseCallback(responseData) {
console.log(responseData)
})
})
我们再来看看DSBridge的调用方式:
var bridge = getJsBridge();
var str=bridge.call("testSyn", {msg: "hello world"});
console.log(str)
甚至只需要一行代码:
console.log(getJsBridge().call("testSyn", {msg: "hello world"}))
对比一下,WebViewJavascriptBridge 的setupWebViewJavascriptBridge函数必须在使用者代码中声明,而此函数主要的作用就是安装bridge,安装成功后,只能在回调中获取bridge对象;这么做有两大缺点:
然而DSBridge 竟是如此优雅!
Natvie 调用 javascript函数
使用WebViewJavascriptBridge时,提供给native调用js函数必须通过bridge在前端进行注册,如:
setupWebViewJavascriptBridge(function(bridge) {
bridge.registerHandler('jsfun', function(data, responseCallback) {
console.log("I am js function, arg is:", data)
responseCallback(data)
})
})
而DSBridge中完全不需要,只需像正常函数一样将其声明为全局函数即可:
function jsfun(data){
console.log("I am js function, arg is:", data)
}
如果你的代码不再全局环境下(在一个闭包中),您只需将函数作为window对象的一个属性即可:
window.jsfun=function(data){
console.log("I am js function, arg is:", data)
}
第二回合 DSBridge完胜!
实现API的方式
WebViewJavascriptBridge需要手动注册所有可供js调用的api,如下:
[self.bridge registerHandler:@"echo" handler:^(id data, WVJBResponseCallback responseCallback) {
//设置js函数的返回值,该值将在js调用时传递的回调中接收
responseCallback(...);
}];
[self.bridge registerHandler:@"api2" handler:^(id data, WVJBResponseCallback responseCallback) {
responseCallback(...);
}];
...
需要注意的是WebViewJavascriptBridge对返回值的处理只能通过block.
我们看看DSBridge,很简单只需要将所有api放到一个类中,然后统一注册即可,还有返回值可以直接返回
//JsApiTest.m
@implementation JsApiTest
- (NSString *) echo:(NSDictionary *) args
{
return @"...";
}
- (NSString *) api2:(NSDictionary *) args
{
return @"...";
}
@end
//统一注册
jsApi=[[JsApiTest alloc] init];
webview.JavascriptInterfaceObject=jsApi;
android端和ios端类似,我们复制一下github.com/gzsll/WebVi… 下的例子(这是android的一个兼容实现):
webView.registerHandler("api1", new WVJBWebView.WVJBHandler() {
@Override
public void request(Object data, WVJBWebView.WVJBResponseCallback callback) {
callback.callback("return value");
}
});
webView.registerHandler("api2", new WVJBWebView.WVJBHandler() {
@Override
public void request(Object data, WVJBWebView.WVJBResponseCallback callback) {
callback.callback("return value");
}
});
...
每个api都要单独注册,我们来看看DSBridge官方给出的例子:
public class JsApi{
//同步api
@JavascriptInterface
String api1(JSONObject jsonObject) throws JSONException {
return jsonObject.getString("msg") + "[syn call]";
}
//异步api
@JavascriptInterface
void api2(JSONObject jsonObject, CompletionHandler handler) throws JSONException {
handler.complete(jsonObject.getString("msg")+" [asyn call]");
}
}
//注册
webView.setJavascriptInterface(new JsApi());
DSBridge不用每个api都去手动注册,只需要将所有api放到一个类中,将需要供js调用的api添加@JavascriptInterface标注即可(出于安全的考虑,关于这个话题,如果有兴趣可自行google)。
对比一下:
WebViewJavascriptBridge为每个api都要单独注册,并且返回值只能通过block或委托;而DSBridge这种统一注册的方式不仅简洁方便,而且所有api放在同一个类中也有助于代码管理,清晰,优雅;而DSBridge同步api可以直接返回值!
综上所述,无论在前端还是在ios/android,在易用性上 DSBridge完胜WebViewJavascriptBridge。
Native调用js方面,两者差异不大,具体请参考github说明,持平。
这是DSBridge大显神威的主要地方,在比较之前,我们先来讨论一下同步/异步对程序流程的影响:如果你是一名前端开发者,想必对异步、回调这些概念已经有所体会,如果你接触过node, 那应该早已深入骨髓。javascript语言从设计为单线程开始就决定了它将与异步相伴终老的局面。纵观晚上node被黑的所有理由中,“过多依赖异步导致代码流程难以控制”首当其冲,随然 node社区一些非常知名的第三方包,有很多都和异步转同步、流程控制相关,如bluebird、async,fibers等. 甚至最新的ECMA标准中引入的generator/yield 、 Promise、async/await等都和同步/异步有关。javascript最成功的地方是异步然而最受诟病的也是异步,当然,我不打算过多讨论js语言,我们回来,看看过多的异步会带来什么问题,试想如下的需求:
假设有一个内嵌在端上的web功能模块,有多个页面,而多个页面之间需要通过端共享一些数据 ,为了说明问题,我们不使用h5本地存储。假设前端的逻辑需要在不同的时段从端上获取不同的数据,用WebViewJavascriptBridge的话,代码整体的流程大概会是下面这个样子:
bridge.callHandler('getData', {'key':'name'}, function responseCallback(responseData) {
//执行一些操作
bridge.callHandler('getData', {'key':'age'}, function responseCallback(responseData) {
//执行一些操作
...
bridge.callHandler('getData', {'key':'sex'}, function responseCallback(responseData) {
...
})
})
})
我们来仔细看看这种方式有什么问题,首先,获取数据并非耗时操作,端上的API设计成同步,但是由于WebViewJavascriptBridge前端只支持异步调用方式,所以最终的代码将必然是回调套回调,如果交互变多,这种情况会变的更糟,这就是异步编程最大的缺点,流程难以理解,但人类的思维模式是同步的,第一步做什么,然后第二步做什么,用代码来描述就是第一句执行什么,然后第二句执行什么,然而这种基于回调的方式却正好相反,简单一点的应用,不会有太大问题,但是稍微复杂一点的应用,事情将会变的很糟。
然而,DSBridge让一切变的简单!
我们看看用DSBridge实现上述逻辑的大概样子:
var name=bridge.call('getData', {'key':'name'});
//执行一些操作
var age=bridge.call('getData', {'key':'age'});
//执行一些操作
var sex=bridge.call('getData', {'key':'sex'});
简单清晰!
当然,DSBridge也是支持异步调用的,这通常用于耗时的api调用,调用方式请查看github文档。
到目前为止,据作者所知,跨平台的js bridge中,DSBridge是唯一一个支持异步调用的!这一点吊打包括WebViewJavascriptBridge在内现有的其它几乎所有的js bridge。
从底层的实现来看,WebViewJavascriptBridge是通过iframe和自定义协议在web和native之间传递数据,属于一种间接的做法,因为在android下,js是可以直接和java对象关联通信的,而在ios下UIWebview可以直接使用JavascriptContext实现和安卓类似的功能:js 可以直接和 oc对象关联,而DSBridge在android和ios、uiwebview中是直接通过js和原生对象关联方式直接通信。具体性能虽没有做过专门测试,当从实现方式上来看,DSBridge少了一个iframe中间层,应该会高一些。
关于兼容性,两者差不多,ios都支持7.0以上,DSBridge理论上支持所有android版本,但是考虑到近年来移动端浏览器对h5支持的快速提高,默认的最低版本设置为api16, 你可以根据自己的需求修改这个配置。
通过各方面对比,DSBridge几乎完爆WebViewJavascriptBridge,当然,由于WebViewJavascriptBridge诞生的年代比较早,到现在积累的用户非常多(事实上是世界上目前使用最多的),但是时间在流逝,有些东西必将成为历史,DSBridge的出现也注定着WebViewJavascriptBridge将成为历史,如果DSBridge没有实现这个目标,那只有一个可能,那就是DSBridge的作者对其推广不够。
如果你觉的DSBridge不错,想对这个新丁支持一下,想让更多的人能够知道它,请在github上star一下哦,顶起来。
再次贴出DSBridge项目地址:
DSBridge-IOS:github.com/wendux/DSBr…
DSBridge-Android:github.com/wendux/DSBr…