EasyBridge:一種簡單的js-bridge設(shè)計(jì)方案

EasyBridge是一個簡單易用的js-bridge的工具庫,提供了日常開發(fā)中,JavaScript與Java之間通訊的能力,與其他常見的js-bridge工具庫實(shí)現(xiàn)方案不同,EasyBridge具備以下幾個特點(diǎn):

  • 基于Android WebViewaddJavascriptInterface特性實(shí)現(xiàn)
  • 提供了基于接口粒度的安全管理接口
  • 輕量級,并且簡單易用。以這個工具庫作為依賴,只需要編寫實(shí)際通訊接口

Principle

目前混合開發(fā)的方案包括:

EasyBridge就是一種簡單的JSBridge解決方案。在眾多的解決方案中,基本都是在利用系統(tǒng)的WebView所開放的接口,打開Java與JavaScript通訊的渠道,這些方案的實(shí)現(xiàn)原理分別包括:

  • 攔截onJsPrompt()方法

    當(dāng)WebView中的頁面調(diào)用了JavaScript當(dāng)中的window.prompt()方法的時候,這個方法會被回調(diào)。而且這個方法不僅能獲取到JavaScript傳遞過來的string字符串內(nèi)容,同時也能返回一段string字符串內(nèi)容被JavaScript接收到,是一個相當(dāng)適合構(gòu)建bridge的入口方法。

  • 攔截shouldOverrideUrlLoading()方法

    當(dāng)頁面重新load URL或者頁面的iframe元素重新加載新的URL的時候,這個方法被回調(diào)。

  • addJavascriptInterface()接口

    這個接口簡單卻強(qiáng)大,通過這個接口,我們能夠直接把Java中定義的對象在JavaScript中映射出一個對應(yīng)的對象,使其直接調(diào)用Java當(dāng)中的方法,但是,在android 4.1及之前的版本存在著嚴(yán)重的漏洞,所以一直被忽視。

EasyBridge在眾多的解決方案中,最終了選擇了addJavascriptInterface()接口作為方案的基礎(chǔ),主要基于以下幾點(diǎn)考量:

  • 目前Android版本已經(jīng)到了9.0版本,市面上Android4.4之前的版本手機(jī)占有率已經(jīng)很低,很多業(yè)務(wù)都已經(jīng)把最低兼容版本定在了4.2以上,在這種情況下可以不考量4.1以下存在的漏洞問題;
  • addJavascriptInterface()能夠提供最簡單的同步調(diào)用
  • addJavascriptInterface()evaluateJavascript()/loadUrl結(jié)合,能夠帶來更加簡單的異步調(diào)用的解決方案

方案結(jié)構(gòu)

EasyBridge的方案結(jié)構(gòu)如下圖所示:

easybridge-architecture.png

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

  • easyBridge

    在頁面加載到25%以上的時候(onProgressChanged()),通過執(zhí)行工具庫中的一個js文件注入的。這個對象主要的作用是定義了業(yè)務(wù)頁面的JavaScript代碼調(diào)用native的Java代碼的規(guī)范入口,對象中定義的一個最關(guān)鍵的函數(shù)就是callHandler(handlerName, args, callback),這就是橋梁的入口。實(shí)際上在這個方法的內(nèi)部,最終就是通過下面的_easybridge對象進(jìn)入到Java代碼層。

  • _easybridge

    通過addJavascriptInterface()映射和注入的一個對象,這個對象提供了實(shí)質(zhì)的入口方法enqueue(),在這個方法當(dāng)中代碼的路線從JavaScript層進(jìn)入到了Java層,開啟了兩者的交互。

接口分發(fā)

實(shí)際上,我們可以通過@JavascriptInterface注解開放很多的接口給JavaScript層調(diào)用,也可以通過addJavascriptInterface()映射多個Java對象到JavaScript層,但是為了維護(hù)簡單和通訊方便,EasyBridge的設(shè)計(jì)只提供了一個入口和一個出口。所有需要開放給JavaScript層的功能,都是通過構(gòu)建接口實(shí)例進(jìn)行處理。

接口的定義如下:

public interface BridgeHandler {

    String getHandlerName();

    void onCall(String parameters, ResultCallBack callBack);

    SecurityPolicyChecker securityPolicyChecker();
}

實(shí)際的工作流程如下圖所示:


handler-execute.png

最開始初始化的時候需要注冊所有可以被JavaScript層調(diào)用的業(yè)務(wù)接口。在運(yùn)行的過程中,enqueue()入口當(dāng)中會根據(jù)協(xié)議定義,通過接口名稱找到對應(yīng)的處理接口實(shí)例,并觸發(fā)接口響應(yīng)。并且最終的接口響應(yīng)都在入口處進(jìn)行回傳。因此,實(shí)際上,_easybridge對象(在Java層中,其實(shí)是EasyBridge的實(shí)例)就是一個樞紐站,做任務(wù)的分派和結(jié)果的傳遞。

安全控制

EasyBridge的安全檢查通過實(shí)現(xiàn)下面的接口對象來完成:

public interface SecurityPolicyChecker {
    boolean check(String url, String parameters);
}

內(nèi)部提供了兩級的安全控制策略,分別對應(yīng)全局的安全控制策略和基于接口粒度的安全控制策略:

全局安全控制策略

EasyBridgeWebView對象當(dāng)中,持有SecurityPolicyChecker用于進(jìn)行全局的安全檢查。全局的安全檢查在EasyBridge的內(nèi)部發(fā)生在以下兩個時機(jī):

  1. Java注入bridge的時候

    根據(jù)當(dāng)前加載的頁面url和參數(shù)信息,判斷是否需要在頁面中注入bridge。如果被安全規(guī)則攔截,則之前通過addJavaScriptInterface()注入的對象會被移除,也不會向頁面中注入bridge對象,確保安全。

  2. JavaScript調(diào)用具體的Java接口的時候

    在執(zhí)行具體的Java開放的接口方法之前, 也會先觸發(fā)全局的安全檢查規(guī)則。

基于接口粒度的安全控制策略

每一個BridgeHandler實(shí)例也都會持有一個SecurityPolicyChecker的實(shí)例,用于針對具體的開發(fā)接口設(shè)置安全檢查規(guī)則。每一個接口在接收到分派的指令之前,會先調(diào)用其安全控制策略,根據(jù)當(dāng)前加載的頁面地址以及傳入的指令參數(shù)判斷是否需要進(jìn)行指令的分派,否則將會直接命令安全受限,錯誤返回,結(jié)果調(diào)用。

Feture

?? 注入jsbridge重試機(jī)制

?? 使用apt技術(shù)注冊handler

?? JavaScript調(diào)用Java功能

?? Java調(diào)用JavaScript功能

?? 安全控制策略

關(guān)于EasyBridge的feature的詳細(xì)說明以及整個工具庫的使用,可以參考我的倉庫中的說明。

整個工具庫比較簡單和小巧,目前開發(fā)自測階段基本能夠滿足業(yè)務(wù)的需求(但還不確定存在哪些坑),歡迎大家對這種實(shí)現(xiàn)方案存在的問題提出改進(jìn)的意見,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容