JSBridge是Native代碼與JS代碼的通訊橋樑。目前的一種統一方案是:H5觸發url scheme->Native捕獲url scheme->原生分析,執行->原生調用h5。以下圖html
查看大圖 前端
https://dailc.github.io/staticResource/blog/hybrid/jsbridge/img_hybrid_base_jsbridgePrinciple_3.pngandroid
上圖中有提到url scheme這個概念,那這究竟是什麼呢?git
基於上述的基本原理,如今開始設計一種JSBridge的實現github
要實現JSBridge,咱們能夠進行關鍵步驟分析web
以下圖:api
查看大圖 數組
https://dailc.github.io/staticResource/blog/hybrid/jsbridge/img_hybrid_base_jsbridgePrinciple_3.png微信
咱們規定,JS和Native之間的通訊必須經過一個H5全局對象JSbridge來實現,該對象有以下特色網絡
var JSBridge = window.JSBridge || (window.JSBridge = {});
registerHandler(String,Function)
H5調用,註冊本地JS方法,註冊後Native可經過JSBridge調用。調用後會將方法註冊到本地變量messageHandlers
中callHandler(String,JSON,Function)
H5調用,調用原生開放的api,調用後實際上仍是本地經過url scheme觸發。調用時會將回調id存放到本地變量responseCallbacks
中_handleMessageFromNative(JSON)
Native調用,原生調用H5頁面註冊的方法,或者通知H5頁面執行回調方法查看大圖
https://dailc.github.io/staticResource/blog/hybrid/jsbridge/img_hybrid_base_jsbridgePrinciple_2.png
在第一步中,咱們定義好了全局橋對象,能夠咱們是經過它的callHandler方法來調用原生的,那麼它內部經歷了一個怎麼樣的過程呢?以下
在執行callHandler時,內部經歷瞭如下步驟:
responseCallbacks
中//url scheme的格式如 //基本有用信息就是後面的callbackId,handlerName與data //原生捕獲到這個scheme後會進行分析 var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data
//建立隱藏iframe過程 var messagingIframe = document.createElement('iframe'); messagingIframe.style.display = 'none'; document.documentElement.appendChild(messagingIframe); //觸發scheme messagingIframe.src = uri;
* 注意,正常來講是能夠經過window.location.href達到發起網絡請求的效果的,可是有一個很嚴重的問題,就是若是咱們連續屢次修改window.location.href的值,在Native層只能接收到最後一次請求,前面的請求都會被忽略掉。因此JS端發起網絡請求的時候,須要使用iframe,這樣就能夠避免這個問題。
在上一步中,咱們已經成功在H5頁面中觸發scheme,那麼Native如何捕獲scheme被觸發呢?
根據系統不一樣,Android和iOS分別有本身的處理方式
在Android中(WebViewClient裏),經過shouldoverrideurlloading
能夠捕獲到url scheme的觸發
public boolean shouldOverrideUrlLoading(WebView view, String url){ //讀取到url後自行進行分析處理 //若是返回false,則WebView處理連接url,若是返回true,表明WebView根據程序來執行url return true; }
另外,Android中也能夠不經過iframe.src來觸發scheme,android中能夠經過window.prompt(uri, "");
來觸發scheme,而後Native中經過重寫WebViewClient的onJsPrompt
來獲取uri
iOS中,UIWebView有個特性:在UIWebView內發起的全部網絡請求,均可以經過delegate函數在Native層獲得通知。這樣,咱們能夠在webview中捕獲url scheme的觸發(原理是利用 shouldStartLoadWithRequest)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSURL *url = [request URL]; NSString *requestString = [[request URL] absoluteString]; //獲取利潤url scheme後自行進行處理
return YES;
}
以後Native捕獲到了JS調用的url scheme,接下來就該到下一步分析url了
在前面的步驟中,Native已經接收到了JS調用的方法,那麼接下來,原生就應該按照定義好的數據格式來解析數據了
url scheme的格式,前面已經提到。Native接收到Url後,能夠按照這種格式將回調參數id、api名、參數提取出來,而後按以下步驟進行
{responseId:回調id,responseData:回調數據}
responseId String型
H5頁面中對應須要執行的回調函數的id,在H5中生成url scheme時就已經產生responseData JSON型
Native須要傳遞給H5的回調數據,是一個JSON格式: {code:(整型,調用是否成功,1成功,0失敗),result:具體須要傳遞的結果信息,能夠爲任意類型,msg:一些其它信息,如調用錯誤時的錯誤信息}
第五步Native如何調用JS
到了這一步,就該Native經過JSBridge調用H5的JS方法或者通知H5進行回調了,具體以下
//將回調信息傳給H5 JSBridge._handleMessageFromNative(messageJSON);
如上,其實是經過JSBridge的_handleMessageFromNative傳遞數據給H5,其中的messageJSON數據格式根據兩種不一樣的類型,有所區別,以下
數據格式爲:上文中的回調的JSON格式
Native主動調用H5方法時,數據格式是:{handlerName:api名,data:數據,callbackId:回調id}
handlerName String型
須要調用的,h5中開放的api的名稱data JSON型
須要傳遞的數據,固定爲JSON格式(由於咱們固定H5中註冊的方法接收的第一個參數必須是JSON,第二個是回調函數)注意,這一步中,若是Native調用的api是h5沒有註冊的,h5頁面上會有對應的錯誤提示。
另外,H5調用Native時,Native處理完畢後必定要及時通知H5進行回調,要否則這個回調函數不會自動銷燬,多了後會引起內存泄漏。
前面有提到Native主動調用H5中註冊的api方法,那麼h5中怎麼註冊供原生調用的api方法呢?格式又是什麼呢?以下
//註冊一個測試函數 JSBridge.registerHandler('testH5Func',function(data,callback){ alert('測試函數接收到數據:'+JSON.stringify(data)); callback&&callback('測試回傳數據...'); });
如上述代碼爲註冊一個供原生調用的api
如上代碼,註冊的api參數是(data,callback)
其中第一個data即原生傳過來的數據,第二個callback是內部封裝過一次的,執行callback後會觸發url scheme,通知原生獲取回調信息
github上有一個開源項目,它裏面的JSBridge作法在iOS上進一步優化了,因此參考他的作法,這裏進一步進行了完善。地址github-WebViewJavascriptBridge
大體思路就是
這種完善後的流程和之前有所區別,以下
查看大圖
https://dailc.github.io/staticResource/blog/hybrid/jsbridge/img_hybrid_base_jsbridgePrinciple_4.png
查看大圖
https://dailc.github.io/staticResource/blog/hybrid/jsbridge/img_hybrid_base_jsbridgePrinciple_5.png
因爲此次完善的核心是:Native主動調用JS函數,並獲取返回值。而在Android4.4之前,Android是沒有這個功能的,因此並不徹底適用於Android
因此通常會進行一個兼容處理,Android中採用之前的scheme傳法,iOS使用完善後的方案(也便於4.4普及後後續的完善)
上述分析了JSBridge的實現流程,那麼實際項目中,咱們就應該結合上述兩種,針對Android和iOS的不一樣狀況,統一出一種完整的方案,以下
查看大圖
https://dailc.github.io/staticResource/blog/hybrid/jsbridge/img_hybrid_base_jsbridgePrinciple_6.png
如上圖,結合上述方案後有了一套統一JSBridge方案
前面提到的JSBridge都是基於url scheme的,但其實若是不考慮Android4.2如下,iOS7如下,其實也能夠用另外一套方案的,以下
固然了,這只是一種可行的方案,多一種選擇而已,具體實現流程請參考前面系列文章,本文再也不贅述
本文中包括兩個示例,一個是基礎版本的JSBridge實現,一個是完整版本的JSBridge實現(包括JS,Android端實現等)。另外,這套方案是有iOS實踐經驗的,能夠仿照着Android端編碼(或者參考WebViewJavascriptBridge
這個示例),例子裏就暫時沒有提供了。
原文:http://www.jianshu.com/p/dd12692fcfd6