EasyBridge:一種簡單的js-bridge方案設計

日前在總結項目中已有的jsbridge方案的時候,由於以爲存在諸多不合理的地方,因此針對業務的場景以及實際的狀況,重寫了一個簡單易用的js-bridge方案,命名爲EasyBridgejava

EasyBridge是一個簡單易用的js-bridge的工具庫,提供了平常開發中,JavaScript與Java之間通信的能力,與其餘常見的js-bridge工具庫實現方案不一樣,EasyBridge具有如下幾個特色:react

  • 基於Android WebViewaddJavascriptInterface特性實現
  • 提供了基於接口粒度的安全管理接口
  • 輕量級,而且簡單易用。以這個工具庫做爲依賴,只須要編寫實際通信接口

實現原理說明

混合開發一直是工業界移動端開發比較看好的技術手段,結合h5的特性,可以更好的支持業務發展的須要,不只快速上線、部署功能並且可以快速響應線上的bug。目前混合開發的方案包括:android

EasyBridge就是一種簡單的JSBridge解決方案。在衆多的解決方案中,都是在利用系統的WebView所開放的權限和接口,打開Java與JavaScript通信的渠道,這些方案的實現原理分別包括:git

  • 攔截onJsPrompt()方法github

    WebView中的頁面調用了JavaScript當中的window.prompt()方法的時候,這個方法會被回調。並且這個方法不只能獲取到JavaScript傳遞過來的string字符串內容,同時也能返回一段string字符串內容被JavaScript接收到,是一個至關適合構建bridge的入口方法。web

  • 攔截shouldOverrideUrlLoading()方法chrome

    當頁面從新load URL或者頁面的iframe元素從新加載新的URL的時候,這個方法被回調。apache

  • addJavascriptInterface()接口react-native

    這個接口簡單卻強大,經過這個接口,咱們可以直接把Java中定義的對象在JavaScript中映射出一個對應的對象,使其直接調用Java當中的方法,可是,在android 4.1及以前的版本存在着嚴重的漏洞,因此一直被忽視。安全

EasyBridge在衆多的解決方案中,最終了選擇了addJavascriptInterface()接口做爲方案的基礎,主要基於如下幾點考量:

  • 目前Android版本已經到了9.0版本,市面上Android4.4以前的版本手機佔有率已經很低,不少業務都已經把最低兼容版本定在了4.2以上,所以不須要考量4.1如下存在的漏洞問題;
  • addJavascriptInterface()可以提供最簡單的同步調用
  • addJavascriptInterface()evaluateJavascript()/loadUrl結合,可以帶來更加簡單的異步調用的解決方案

方案設計說明

EasyBridge最終方案實現,只支持了異步調用的方式,主要是基於如下的考量:

  • 同步的調用能夠轉化爲異步調用的方式,保留一種調用方式會使得整個方案更加簡單;

方案結構

EasyBridge的方案結構以下圖所示:

EasyBridge總共會向頁面中注入兩個JavaScript對象,:

  • easyBridge

    在頁面加載完成onPageFinished()回調的時候,經過執行工具庫中的一個js文件注入的。這個對象主要的做用是定義了業務頁面的JavaScript代碼調用native的Java代碼的規範入口,對象中定義的一個最關鍵的函數就是callHandler(handlerName, args, callback),這就是橋樑的入口。實際上在這個方法的內部,最終就是經過下面的**_easybridge**對象進入到Java代碼層。

  • _easybridge

    經過addJavascriptInterface()映射和注入的一個對象,這個對象提供了實質的入口方法enqueue(),在這個方法當中代碼的路線從JavaScript層進入到了Java層,開啓了二者的交互。

接口分發

實際上,咱們能夠經過@JavascriptInterface註解開放不少的接口給JavaScript層調用,也能夠經過addJavascriptInterface()映射多個Java對象到JavaScript層,可是爲了維護簡單和通信方便,EasyBridge的設計只提供了一個入口和一個出口。全部須要開放給JavaScript層的功能,都是經過構建接口實例進行處理。

接口的定義以下:

public interface BridgeHandler {

    String getHandlerName();

    void onCall(String parameters, ResultCallBack callBack);

    SecurityPolicyChecker securityPolicyChecker();
}
複製代碼

實際的工做流程以下圖所示:

最開始初始化的時候須要註冊全部能夠被JavaScript層調用的業務接口。在運行的過程當中,enqueue()入口當中會根據協議定義,經過接口名稱找到對應的處理接口實例,並觸發接口響應。而且最終的接口響應都在入口處進行回傳。所以,實際上,_easybridge對象(在Java層中,實際上是EasyBridge的實例)就是一個樞紐站,作任務的分派和結果的傳遞。

安全控制

每個BridgeHandler實例,均可以定義本身的安全控制策略,對應的是一個SecurityPolicyChecker的實例,其定義以下:

public interface SecurityPolicyChecker {
    boolean check(String url, String parameters);
}
複製代碼

每個接口在接收到分派的指令以前,會先調用其安全控制策略,根據當前加載的頁面地址以及傳入的指令參數判斷是否須要進行指令的分派,不然將會直接命令安全受限,錯誤返回,結果調用。

方案使用

EasyBridge是一個極其簡單易用的方案,只須要簡單的幾步便可具有JavaScript層與Java層通信的能力。在引入EasyBridge庫做爲依賴以後:

  1. 繼承/直接使用EasyBridgeWebView

    EasyBridgeWebView是功能的承載者,負責了bridge對象的注入,以及handler接口的管理(內部使用`EasyBridge對象管理)

  2. 根據業務以及協議定義實現對應的BridgeHandler實例

  3. 在加載第一步的webview的實例頁面綁定第二步構造的handler實例

以上三步即完成了全部的工做。

若是你須要調試這個方案的實際工做,你能夠在把手機鏈接到電腦以後,使用chrome進行調試。EasyBridge會把傳遞的結果信息以及錯誤信息打印在控制檯之上。你將會很容易的感知和發現問題。關於在Chrome中調試web頁面,你能夠參考官方的教程文檔Remote Debugging WebViews

關於這個方案目前已經具有的feature,以及demo,歡迎訪問個人GitHub倉庫EasyBridge。同時歡迎你們對這個方案的設計和實現提出大家的改進意見,謝謝

相關文章
相關標籤/搜索