之前一直在使用JsBridge,也知道大概的原理,但是沒有去深究。最近有空研究了一下。以Android和H5兩端的視角分析一下其中的原理
需求背景
Native 加載 H5 頁面,H5頁面可以調用Native的方法,Native也可以調用H5頁面的方法,并且可以傳遞數據和設置回調。
這基本上是一個Hybrid應用所具有的基本功能,下面就來分析下具體的實現過程。
官方方案
- Android 4.2 之前有漏洞,js可以訪問任何public方法,最主要的是getClass方法,至于具體情況,有興趣的可以網絡search一下。
- 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中怎么判斷我要執行哪一個方法呢?調用的方法有沒有限制?下面就來介紹下我司的實際解決方案
- 在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 - 自定義webView 添加UserAgent 標識,這里定為 "droid"(下同) ,這里的目的是讓js識別是需要處理的url
- Native load(url)
小總結:以上三部是在Native需要做的事情,主要就是準備給js調用的方法,加載url - 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存在的意義--提供一個橋梁,讓兩邊通信
調用
H5調用Native
- js 提供一個方法bridgecall (下同),用來拼裝一個url 包括 type: 調用方法/回調方法 module method args。
- bridgecall 會 document.createElement 一個iFrame,設置它的src為上面拼裝的url,這樣就會發起一個url的請求,隨后移除掉這個iFrame
- H5調用js注冊的module的方法,會統一調用到invokeNative方法調用bridgecall方法,從而發起一個url請求
- webViewClient的shouldOverrideLoading()方法會攔截到url,然后解析url,找到相應的Method執行。
Native調用 H5
- loadurl("javascript:js的方法invokeJSMethod(module,method,args)"),這里的args,如果有回調函數,bridge會存一個map<callbackidFormat,callBackFun> ,然后把callbackIdFormat作為參數加載args中。
- js 執行invokeJSMethod方法,解析callbackIdFormat,如果匹配到的話說明有回調函數,在執行完方法后會把callbackIdFormat作為參數,執行callbackNavite方法
- callbackNative 到invokeNative方法調用bridgecall發起url請求
- webViewClient的shouldOverrideLoading()方法會攔截到url,解析到時回調請求,就去獲取callbackidFormat對應的callBackFun。
總結:
H5調用Native就是拼裝url,發起請求,Native攔截去請求Native的方法,如需要回調,之前url帶上回調函數,去執行js的函數,跟Native調用H5一樣。
Native調用H5就是執行H5中的方法,若需要回調,跟H5調用Native一樣