巔峰對決 DSBridge vs WebViewJavascriptBridge

隨著HTML5的不斷普及及優(yōu)化,以及移動端對動態(tài)化的需求越來越大,開發(fā)者經(jīng)常需要在app中嵌入一些網(wǎng)頁,然后會在web和native之間進(jìn)行交互,如傳遞數(shù)據(jù),調(diào)用函數(shù),而連接web與native需要一個橋梁。DSBridge android 和 ios 版上兩篇文章已經(jīng)介紹完畢,既然DSBridge 號稱是地球上最好用的跨平臺開源 js bridge,那么今天就將它和現(xiàn)在的老大 WebViewJavascriptBridge 全方位對比一下,看看究竟是初生牛犢不怕虎還是嘩眾取寵,又或是實(shí)至名歸......

DSBridge vs WebViewJavascriptBridge

下面是DSBridge和WebViewJavascriptBridge的github地址:

DSBridge-IOS:https://github.com/wendux/DSBridge-IOS
DSBridge-Android:https://github.com/wendux/DSBridge-Android

WebViewJavascriptBridge(ios) https://github.com/marcuswestin/WebViewJavascriptBridge

約定:一下術(shù)語“端”指的是native(ios/android), 而 “前端” 特指 web端(javascript);

好橋的標(biāo)準(zhǔn)

  1. 跨平臺;這是首要的,必須同時(shí)支持ios/android,因?yàn)榫W(wǎng)頁會嵌入到端上,而網(wǎng)頁代碼只有一份,同樣的javascript代碼必須能同時(shí)保證能和ios/android正常通信。
  2. 三端易用;三端指ios 、android和前端。這很重要,因?yàn)樗鼤苯佑绊懝ぷ髁亢痛a量。
  3. 雙向調(diào)用;js可以調(diào)用native, native可以調(diào)用js;
  4. 支持同步/異步調(diào)用;同步用于一般任務(wù),異步主要用于耗時(shí)任務(wù),調(diào)用方式不同會影響前段代碼流程。
  5. 性能、兼容性等。

跨平臺

通過github上的信息可以看到DSBridge官方是同時(shí)支持ios/android的,但是注意到,DSBridge ios版是支持wkwebview的,而wkwebview也是可以用在osx中,也就是說DSBridge ios版也是可以用于mac開發(fā)的。也就是說DSBridge同時(shí)支持:ios/android/osx。再來看看WebViewJavascriptBridge,官方說明是支持ios/osx的,但WebViewJavascriptBridge并不支持android, 當(dāng)然,由于WebViewJavascriptBridge的人氣實(shí)在太高,也有一些人在android上實(shí)現(xiàn)了兼容的版本如這個點(diǎn)我,但是總的來說,并非一家之作,這可能會給日后維護(hù)帶來問題。而DSBridge是同一個作者,如果將來更新時(shí)可以保證雙端同步。第一回合,DSBridge勝!

易用性

Javascript Bridge比較特殊,因?yàn)樗瑫r(shí)涉及三端,讓三個端使用起來都比較容易是很有挑戰(zhàn)的一個任務(wù),這不僅是能力層面,也和接口和使用方式的設(shè)計(jì)息息相關(guān),當(dāng)然能在三端之間做出最好的平衡,也對作者能力要求比較高(必須同時(shí)了解ios/android/web)。 接下來我們分別從三端的接口和使用方式做一個詳細(xì)對比。

web端

Javascript調(diào)用native

假設(shè)native有個回顯信息的函數(shù),簽名如下:

String echo(JSONObject args); //先用java描述

該函數(shù)功能是js調(diào)用時(shí)傳遞一個字符串msg給端,然后端上再返回 "you put string "+msg; 參數(shù)以json傳遞。

先來看看WebViewJavascriptBridge的調(diào)用方式

第一步:復(fù)制 setupWebViewJavascriptBridge 函數(shù)聲明到你的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)
}

第二步:調(diào)用 setupWebViewJavascriptBridge,在回調(diào)中獲得bridge, 然后通過bridge調(diào)用native方法:

setupWebViewJavascriptBridge(function(bridge) {
    bridge.callHandler('echo', {'msg':'hello world'}, function responseCallback(responseData) {
        console.log(responseData)
    })
})

我們再來看看DSBridge的調(diào)用方式

var bridge = getJsBridge();
var str=bridge.call("testSyn", {msg: "hello world"});
console.log(str)

甚至只需要一行代碼:

console.log(getJsBridge().call("testSyn", {msg: "hello world"}))

對比一下,WebViewJavascriptBridge 的setupWebViewJavascriptBridge函數(shù)必須在使用者代碼中聲明,而此函數(shù)主要的作用就是安裝bridge,安裝成功后,只能在回調(diào)中獲取bridge對象;這么做有兩大缺點(diǎn):

  1. bridge安裝細(xì)節(jié)不應(yīng)暴漏給用戶;setupWebViewJavascriptBridge不應(yīng)該由用戶聲明,這個函數(shù)從來都不變,安裝的過程應(yīng)該由sdk去做,這樣強(qiáng)制要求用戶自己聲明調(diào)用不僅不符合職責(zé)分離的軟件設(shè)計(jì)原則,而且還會造成代碼冗余,試想每個網(wǎng)頁中都要加這么一塊代碼,不僅麻煩,而且也浪費(fèi)帶寬。(可能你不在乎用戶的流量,但是如果你的用戶很多而服務(wù)器、帶寬資源有限,這對服務(wù)器來說也是一個問題,筆者阿里云的ecs帶寬只有1兆,而網(wǎng)站就跑了六個,同時(shí)在線人數(shù)超過200人沒人將會感覺到卡,如果你是土豪,那無視這個吧)。
  2. 不能直接獲取返回值;這點(diǎn)很重要,只能通過回調(diào)方式處理返將直接影響前端代碼組織邏輯,將會在后面同步/異步對比部分詳細(xì)討論。

然而DSBridge 竟是如此優(yōu)雅!

Natvie 調(diào)用 javascript函數(shù)

使用WebViewJavascriptBridge時(shí),提供給native調(diào)用js函數(shù)必須通過bridge在前端進(jìn)行注冊,如:

setupWebViewJavascriptBridge(function(bridge) {
    bridge.registerHandler('jsfun', function(data, responseCallback) {
        console.log("I am js function, arg is:", data)
        responseCallback(data)
    })
})

而DSBridge中完全不需要,只需像正常函數(shù)一樣將其聲明為全局函數(shù)即可:

function jsfun(data){
  console.log("I am js function, arg is:", data)
}

如果你的代碼不再全局環(huán)境下(在一個閉包中),您只需將函數(shù)作為window對象的一個屬性即可:

window.jsfun=function(data){
  console.log("I am js function, arg is:", data)
}

第二回合 DSBridge完勝!

IOS端

實(shí)現(xiàn)API的方式

WebViewJavascriptBridge需要手動注冊所有可供js調(diào)用的api,如下:

[self.bridge registerHandler:@"echo" handler:^(id data, WVJBResponseCallback responseCallback) {
   //設(shè)置js函數(shù)的返回值,該值將在js調(diào)用時(shí)傳遞的回調(diào)中接收 
   responseCallback(...);
}];
[self.bridge registerHandler:@"api2" handler:^(id data, WVJBResponseCallback responseCallback) {
    responseCallback(...);
}];
...

需要注意的是WebViewJavascriptBridge對返回值的處理只能通過block.

我們看看DSBridge,很簡單只需要將所有api放到一個類中,然后統(tǒng)一注冊即可,還有返回值可以直接返回

//JsApiTest.m
@implementation JsApiTest
- (NSString *) echo:(NSDictionary *) args
{
    return @"...";
}
- (NSString *) api2:(NSDictionary *) args 
{
   return @"..."; 
}
@end
//統(tǒng)一注冊
jsApi=[[JsApiTest alloc] init];
webview.JavascriptInterfaceObject=jsApi;

Android端

android端和ios端類似,我們復(fù)制一下https://github.com/gzsll/WebViewJavascriptBridge 下的例子(這是android的一個兼容實(shí)現(xiàn)):

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都要單獨(dú)注冊,我們來看看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調(diào)用的api添加@JavascriptInterface標(biāo)注即可(出于安全的考慮,關(guān)于這個話題,如果有興趣可自行g(shù)oogle)。

對比一下:

WebViewJavascriptBridge為每個api都要單獨(dú)注冊,并且返回值只能通過block或委托;而DSBridge這種統(tǒng)一注冊的方式不僅簡潔方便,而且所有api放在同一個類中也有助于代碼管理,清晰,優(yōu)雅;而DSBridge同步api可以直接返回值!

綜上所述,無論在前端還是在ios/android,在易用性上 DSBridge完勝WebViewJavascriptBridge。

Native調(diào)用js

Native調(diào)用js方面,兩者差異不大,具體請參考github說明,持平。

支持同步/異步調(diào)用支持

這是DSBridge大顯神威的主要地方,在比較之前,我們先來討論一下同步/異步對程序流程的影響:如果你是一名前端開發(fā)者,想必對異步、回調(diào)這些概念已經(jīng)有所體會,如果你接觸過node, 那應(yīng)該早已深入骨髓。javascript語言從設(shè)計(jì)為單線程開始就決定了它將與異步相伴終老的局面。縱觀晚上node被黑的所有理由中,“過多依賴異步導(dǎo)致代碼流程難以控制”首當(dāng)其沖,隨然 node社區(qū)一些非常知名的第三方包,有很多都和異步轉(zhuǎn)同步、流程控制相關(guān),如bluebird、async,fibers等. 甚至最新的ECMA標(biāo)準(zhǔn)中引入的generator/yield 、 Promise、async/await等都和同步/異步有關(guān)。javascript最成功的地方是異步然而最受詬病的也是異步,當(dāng)然,我不打算過多討論js語言,我們回來,看看過多的異步會帶來什么問題,試想如下的需求:

假設(shè)有一個內(nèi)嵌在端上的web功能模塊,有多個頁面,而多個頁面之間需要通過端共享一些數(shù)據(jù) ,為了說明問題,我們不使用h5本地存儲。假設(shè)前端的邏輯需要在不同的時(shí)段從端上獲取不同的數(shù)據(jù),用WebViewJavascriptBridge的話,代碼整體的流程大概會是下面這個樣子:

bridge.callHandler('getData', {'key':'name'}, function responseCallback(responseData) {
    //執(zhí)行一些操作
    bridge.callHandler('getData', {'key':'age'}, function responseCallback(responseData) {
        //執(zhí)行一些操作
        ...
        bridge.callHandler('getData', {'key':'sex'}, function responseCallback(responseData) {
          ...
        })
    })
})

我們來仔細(xì)看看這種方式有什么問題,首先,獲取數(shù)據(jù)并非耗時(shí)操作,端上的API設(shè)計(jì)成同步,但是由于WebViewJavascriptBridge前端只支持異步調(diào)用方式,所以最終的代碼將必然是回調(diào)套回調(diào),如果交互變多,這種情況會變的更糟,這就是異步編程最大的缺點(diǎn),流程難以理解,但人類的思維模式是同步的,第一步做什么,然后第二步做什么,用代碼來描述就是第一句執(zhí)行什么,然后第二句執(zhí)行什么,然而這種基于回調(diào)的方式卻正好相反,簡單一點(diǎn)的應(yīng)用,不會有太大問題,但是稍微復(fù)雜一點(diǎn)的應(yīng)用,事情將會變的很糟。

然而,DSBridge讓一切變的簡單!

我們看看用DSBridge實(shí)現(xiàn)上述邏輯的大概樣子:

var name=bridge.call('getData', {'key':'name'});
//執(zhí)行一些操作
var age=bridge.call('getData', {'key':'age'});
//執(zhí)行一些操作
var sex=bridge.call('getData', {'key':'sex'});

簡單清晰!

當(dāng)然,DSBridge也是支持異步調(diào)用的,這通常用于耗時(shí)的api調(diào)用,調(diào)用方式請查看github文檔。

到目前為止,據(jù)作者所知,跨平臺的js bridge中,DSBridge是唯一一個支持同步調(diào)用的!這一點(diǎn)吊打包括WebViewJavascriptBridge在內(nèi)現(xiàn)有的其它幾乎所有的js bridge

性能、兼容性

從底層的實(shí)現(xiàn)來看,WebViewJavascriptBridge是通過iframe和自定義協(xié)議在web和native之間傳遞數(shù)據(jù),屬于一種間接的做法,因?yàn)樵赼ndroid下,js是可以直接和java對象關(guān)聯(lián)通信的,而在ios下UIWebview可以直接使用JavascriptContext實(shí)現(xiàn)和安卓類似的功能:js 可以直接和 oc對象關(guān)聯(lián),而DSBridge在android和ios、uiwebview中是直接通過js和原生對象關(guān)聯(lián)方式直接通信。具體性能雖沒有做過專門測試,當(dāng)從實(shí)現(xiàn)方式上來看,DSBridge少了一個iframe中間層,應(yīng)該會高一些。

關(guān)于兼容性,兩者差不多,ios都支持7.0以上,DSBridge理論上支持所有android版本,但是考慮到近年來移動端瀏覽器對h5支持的快速提高,默認(rèn)的最低版本設(shè)置為api16, 你可以根據(jù)自己的需求修改這個配置。

總結(jié)

通過各方面對比,DSBridge幾乎完爆WebViewJavascriptBridge,當(dāng)然,由于WebViewJavascriptBridge誕生的年代比較早,到現(xiàn)在積累的用戶非常多(事實(shí)上是世界上目前使用最多的),但是時(shí)間在流逝,有些東西必將成為歷史,DSBridge的出現(xiàn)也注定著WebViewJavascriptBridge將成為歷史,如果DSBridge沒有實(shí)現(xiàn)這個目標(biāo),那只有一個可能,那就是DSBridge的作者對其推廣不夠。

如果你覺的DSBridge不錯,想對這個新丁支持一下,想讓更多的人能夠知道它,請?jiān)趃ithub上star一下哦,頂起來。

再次貼出DSBridge項(xiàng)目地址:

DSBridge-IOS:https://github.com/wendux/DSBridge-IOS
DSBridge-Android:https://github.com/wendux/DSBridge-Android

預(yù)告:下一篇我們將繼續(xù)將DSBridge 和 老二( github上2.3k star的 https://github.com/lzyzsd/JsBridge)做一個對比,歡迎持續(xù)關(guān)注。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,461評論 25 708
  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一種新的協(xié)議。它實(shí)...
    香橙柚子閱讀 24,113評論 8 183
  • 前幾天我的同學(xué)發(fā)了一條朋友圈,大意是這樣的:他在知乎上看到,很多人動不動就給公務(wù)員貼標(biāo)簽,如“不勞而獲”、“不肯拼...
    林易子閱讀 950評論 1 2
  • 一 自己終于成為上帝了,這是在他殺了第三個人之后的想法。 起初殺人時(shí),他的雙手還是不住地顫抖,所以動作一點(diǎn)也不利索...
    譚樹君閱讀 333評論 0 1
  • 著名教育家蘇霍姆林斯基曾說過:“沒有科學(xué)研究便不可能有教育工作。如果你想讓教師的勞動工作給他們帶來樂趣,使天天上課...
    悠悠悅耳閱讀 360評論 0 5