之前一直在使用JsBridge,也知道大概的原理,但是沒有去深究。最近有空研究了一下。以Android和H5兩端的視角分析一下其中的原理
需求背景
Native 加載 H5 頁面豪嗽,H5頁面可以調(diào)用Native的方法,Native也可以調(diào)用H5頁面的方法豌骏,并且可以傳遞數(shù)據(jù)和設(shè)置回調(diào)龟梦。
這基本上是一個Hybrid應(yīng)用所具有的基本功能,下面就來分析下具體的實現(xiàn)過程窃躲。
官方方案
- Android 4.2 之前有漏洞计贰,js可以訪問任何public方法,最主要的是getClass方法蒂窒,至于具體情況躁倒,有興趣的可以網(wǎng)絡(luò)search一下荞怒。
- Android 4.2 之后添加了限制,只有@JavascriptInterface注解的方法秧秉,才能在js中訪問褐桌。
簡要說明
如果只考慮Android4.2 之后的版本,可以使用官方方案象迎,雖說增加了安全性荧嵌,但是也不保證絕對安全。另一方面還需要解決Android 4.2之前的版本砾淌,所以就衍生出了第三種解決方案啦撮。
業(yè)界通常的做法就是攔截js的prompt/alert/confirm方法,然后會執(zhí)行到WebChromeClient中相應(yīng)的方法onJsPrompt/onJsAlert/onJsConfirm拇舀,從而做到j(luò)s調(diào)用Native的方法逻族。因為alert和confirm方法在js中是常用的蜻底,prompt方法不常用骄崩,所以一般選擇攔截prompt方法。
如果只是說原理薄辅,那現(xiàn)在就可以結(jié)束了要拂。可是我是一個較真的人站楚,所以有了下文脱惰。如果你也不滿足于上面提到的這些,就看下面的具體分析吧窿春!
自定義方案
加載
上面說了可以通過攔截js的prompt函數(shù)來執(zhí)行Native的onJsPrompt方法拉一,從而執(zhí)行你想執(zhí)行的Native方法。但這僅僅是原理旧乞,在一個App中怎么判斷我要執(zhí)行哪一個方法呢蔚润?調(diào)用的方法有沒有限制?下面就來介紹下我司的實際解決方案
- 在App中定義所有可以被Js訪問的方法尺栖,由于方法來自各個模塊嫡纠,方法名可能重復(fù),所以定義了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 提供根據(jù)注解名找到所有提供給js的Method蠢棱,存放在map<"methodA",MethodA>中,methodA為注解值锌杀,MethodA為Method.Class - 自定義webView 添加UserAgent 標識,這里定為 "droid"(下同) 泻仙,這里的目的是讓js識別是需要處理的url
- Native load(url)
小總結(jié):以上三部是在Native需要做的事情糕再,主要就是準備給js調(diào)用的方法,加載url - H5頁面收到請求url玉转,引入bridge.js(下同) 會去初始化突想,會根據(jù)userAgent判斷是否需要去處理,需要處理的話究抓,通過調(diào)用window.prompt來調(diào)用Native的onJsPrompt方法獲取Native的JavascriptBridge中存儲的modules猾担,然后注冊到bridge.js
小總結(jié):這一步就是js初始化的時候獲取Native的modules,注冊到j(luò)s中
總結(jié):整個流程就是Native和Js兩端各準備一個bridge刺下,Native的bridge提供modules绑嘹,js的bridge注冊Native提供的modules。這就是bridge存在的意義--提供一個橋梁橘茉,讓兩邊通信
調(diào)用
H5調(diào)用Native
- js 提供一個方法bridgecall (下同)工腋,用來拼裝一個url 包括 type: 調(diào)用方法/回調(diào)方法 module method args。
- bridgecall 會 document.createElement 一個iFrame畅卓,設(shè)置它的src為上面拼裝的url擅腰,這樣就會發(fā)起一個url的請求,隨后移除掉這個iFrame
- H5調(diào)用js注冊的module的方法翁潘,會統(tǒng)一調(diào)用到invokeNative方法調(diào)用bridgecall方法趁冈,從而發(fā)起一個url請求
- webViewClient的shouldOverrideLoading()方法會攔截到url,然后解析url拜马,找到相應(yīng)的Method執(zhí)行渗勘。
Native調(diào)用 H5
- loadurl("javascript:js的方法invokeJSMethod(module,method,args)"),這里的args俩莽,如果有回調(diào)函數(shù)旺坠,bridge會存一個map<callbackidFormat,callBackFun> ,然后把callbackIdFormat作為參數(shù)加載args中豹绪。
- js 執(zhí)行invokeJSMethod方法价淌,解析callbackIdFormat,如果匹配到的話說明有回調(diào)函數(shù)瞒津,在執(zhí)行完方法后會把callbackIdFormat作為參數(shù)蝉衣,執(zhí)行callbackNavite方法
- callbackNative 到invokeNative方法調(diào)用bridgecall發(fā)起url請求
- webViewClient的shouldOverrideLoading()方法會攔截到url,解析到時回調(diào)請求巷蚪,就去獲取callbackidFormat對應(yīng)的callBackFun病毡。
總結(jié):
H5調(diào)用Native就是拼裝url,發(fā)起請求屁柏,Native攔截去請求Native的方法啦膜,如需要回調(diào)有送,之前url帶上回調(diào)函數(shù),去執(zhí)行js的函數(shù)僧家,跟Native調(diào)用H5一樣雀摘。
Native調(diào)用H5就是執(zhí)行H5中的方法,若需要回調(diào)八拱,跟H5調(diào)用Native一樣