從Native和H5兩端看JsBridge

之前一直在使用JsBridge,也知道大概的原理,但是沒有去深究。最近有空研究了一下。以Android和H5兩端的視角分析一下其中的原理

需求背景

Native 加載 H5 頁面,H5頁面可以調用Native的方法,Native也可以調用H5頁面的方法,并且可以傳遞數據和設置回調。
這基本上是一個Hybrid應用所具有的基本功能,下面就來分析下具體的實現過程。

官方方案

  1. Android 4.2 之前有漏洞,js可以訪問任何public方法,最主要的是getClass方法,至于具體情況,有興趣的可以網絡search一下。
  2. Android 4.2 之后添加了限制,只有@JavascriptInterface注解的方法,才能在js中訪問。

簡要說明

如果只考慮Android4.2 之后的版本,可以使用官方方案,雖說增加了安全性,但是也不保證絕對安全。另一方面還需要解決Android 4.2之前的版本,所以就衍生出了第三種解決方案。

業界通常的做法就是攔截js的prompt/alert/confirm方法,然后會執行到WebChromeClient中相應的方法onJsPrompt/onJsAlert/onJsConfirm,從而做到js調用Native的方法。因為alert和confirm方法在js中是常用的,prompt方法不常用,所以一般選擇攔截prompt方法。

如果只是說原理,那現在就可以結束了。可是我是一個較真的人,所以有了下文。如果你也不滿足于上面提到的這些,就看下面的具體分析吧!

自定義方案

加載

上面說了可以通過攔截js的prompt函數來執行Native的onJsPrompt方法,從而執行你想執行的Native方法。但這僅僅是原理,在一個App中怎么判斷我要執行哪一個方法呢?調用的方法有沒有限制?下面就來介紹下我司的實際解決方案

  1. 在App中定義所有可以被Js訪問的方法,由于方法來自各個模塊,方法名可能重復,所以定義了module的概念,即定義所有的module,存在Bridge類的map<moduleName,ExportModule>,這里定為JavascriptBridge(下同),并提供給外部獲取modules {"module": moduleName,"methods": ["methodA",....]}
    說明:
    1. 識別method的方式跟Google官方的方案一樣,采用注解的方式。即在method上面添加注解,這里定為JsBridge(下同)
    2. ExportModule 類有 String moduleName; Class moduleClass; 兩個變量
    和一個 private map 提供根據注解名找到所有提供給js的Method,存放在map<"methodA",MethodA>中,methodA為注解值,MethodA為Method.Class
  2. 自定義webView 添加UserAgent 標識,這里定為 "droid"(下同) ,這里的目的是讓js識別是需要處理的url
  3. Native load(url)
    小總結:以上三部是在Native需要做的事情,主要就是準備給js調用的方法,加載url
  4. H5頁面收到請求url,引入bridge.js(下同) 會去初始化,會根據userAgent判斷是否需要去處理,需要處理的話,通過調用window.prompt來調用Native的onJsPrompt方法獲取Native的JavascriptBridge中存儲的modules,然后注冊到bridge.js
    小總結:這一步就是js初始化的時候獲取Native的modules,注冊到js中

總結:整個流程就是Native和Js兩端各準備一個bridge,Native的bridge提供modules,js的bridge注冊Native提供的modules。這就是bridge存在的意義--提供一個橋梁,讓兩邊通信

加載過程.png

調用

H5調用Native

  1. js 提供一個方法bridgecall (下同),用來拼裝一個url 包括 type: 調用方法/回調方法 module method args。
  2. bridgecall 會 document.createElement 一個iFrame,設置它的src為上面拼裝的url,這樣就會發起一個url的請求,隨后移除掉這個iFrame
  3. H5調用js注冊的module的方法,會統一調用到invokeNative方法調用bridgecall方法,從而發起一個url請求
  4. webViewClient的shouldOverrideLoading()方法會攔截到url,然后解析url,找到相應的Method執行。

Native調用 H5

  1. loadurl("javascript:js的方法invokeJSMethod(module,method,args)"),這里的args,如果有回調函數,bridge會存一個map<callbackidFormat,callBackFun> ,然后把callbackIdFormat作為參數加載args中。
  2. js 執行invokeJSMethod方法,解析callbackIdFormat,如果匹配到的話說明有回調函數,在執行完方法后會把callbackIdFormat作為參數,執行callbackNavite方法
  3. callbackNative 到invokeNative方法調用bridgecall發起url請求
  4. webViewClient的shouldOverrideLoading()方法會攔截到url,解析到時回調請求,就去獲取callbackidFormat對應的callBackFun。

總結:
H5調用Native就是拼裝url,發起請求,Native攔截去請求Native的方法,如需要回調,之前url帶上回調函數,去執行js的函數,跟Native調用H5一樣。
Native調用H5就是執行H5中的方法,若需要回調,跟H5調用Native一樣

加載和互相調用.png

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容