EasyBridge:一分鐘快速實作android的混合開發
日前在總結項目中已有的jsbridge方案的時候,因為覺得存在諸多不合理的地方,是以針對業務的場景以及實際的情況,重寫了一個簡單易用的js-bridge方案,命名為EasyBridge
EasyBridge是一個簡單易用的js-bridge的工具庫,提供了日常開發中,JavaScript與Java之間通訊的能力,與其他常見的js-bridge工具庫實作方案不同,EasyBridge具備以下幾個特點:
- 基于Android
的WebView
特性實作addJavascriptInterface
- 提供了基于接口粒度的安全管理接口
- 輕量級,并且簡單易用。以這個工具庫作為依賴,隻需要編寫實際通訊接口
實作原理說明
混合開發一直是工業界移動端開發比較看好的技術手段,結合h5的特性,能夠更好的支援業務發展的需要,不僅快速上線、部署功能而且能夠快速響應線上的bug。目前混合開發的方案包括:
- JSBridge
- Cordova
- React Native
- Flutter
EasyBridge就是一種簡單的JSBridge解決方案。在衆多的解決方案中,都是在利用系統的
WebView
所開放的權限和接口,打開Java與JavaScript通訊的管道,這些方案的實作原理分别包括:
- 攔截
onJsPrompt()
方法
當
中的頁面調用了JavaScript當中的WebView
方法的時候,這個方法會被回調。而且這個方法不僅能擷取到JavaScript傳遞過來的string字元串内容,同時也能傳回一段string字元串内容被JavaScript接收到,是一個相當适合建構bridge的入口方法。window.prompt()
- 攔截
shouldOverrideUrlLoading()
方法
當頁面重新load URL或者頁面的iframe元素重新加載新的URL的時候,這個方法被回調。
-
addJavascriptInterface()
接口
這個接口簡單卻強大,通過這個接口,我們能夠直接把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
在頁面加載完成
回調的時候,通過執行工具庫中的一個js檔案注入的。這個對象主要的作用是定義了業務頁面的JavaScript代碼調用native的Java代碼的規範入口,對象中定義的一個最關鍵的函數就是onPageFinished()
,這就是橋梁的入口。實際上在這個方法的内部,最終就是通過下面的_easybridge對象進入到Java代碼層。callHandler(handlerName, args, callback)
-
_easybridge
通過
映射和注入的一個對象,這個對象提供了實質的入口方法addJavascriptInterface()
,在這個方法當中代碼的路線從JavaScript層進入到了Java層,開啟了兩者的互動。enqueue()
接口分發
實際上,我們可以通過
@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庫作為依賴之後:
- 繼承/直接使用
EasyBridgeWebView
是功能的承載者,負責了bridge對象的注入,以及handler接口的管理(内部使用`EasyBridge對象管理)EasyBridgeWebView
- 根據業務以及協定定義實作對應的
執行個體BridgeHandler
- 在加載第一步的webview的執行個體頁面綁定第二步構造的handler執行個體
以上三步即完成了所有的工作。
如果你需要調試這個方案的實際工作,你可以在把手機連接配接到電腦之後,使用chrome進行調試。EasyBridge會把傳遞的結果資訊以及錯誤資訊列印在控制台之上。你将會很容易的感覺和發現問題。關于在Chrome中調試web頁面,你可以參考官方的教程文檔Remote Debugging WebViews
關于這個方案目前已經具備的feature,以及demo,歡迎通路我的GitHub倉庫EasyBridge。同時歡迎大家對這個方案的設計和實作提出你們的改進意見,謝謝