IOS WebViewJavascriptBridge 使用以及原理分析

本文轉自:https://www.jianshu.com/p/b8d4285395c6html

 

概述

從兩個方面來說:前端

  •  js不能直接調用oc的方法
  •  oc能夠經過以下函數調用js代碼
 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^      __nullable)(__nullable id, NSError * __nullable error))completionHandler;
 
 

咱們從 github上面的demo https://github.com/marcuswestin/WebViewJavascriptBridgejava

來分析一下這個庫是如何實現js交互的。git

詳解

一.  js調用native方法

在概述中說過,js是不能直接調用native的method因此,須要藉助- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType這個方法。這個方法你們不陌生,每次在從新定向URL的時候,這個方法就會被觸發,一般狀況,咱們會在這裏作一些攔截,用來完成js和本地的間接交互。那麼WebViewJavascriptBridge也不另外,也是這麼作。github

 

 
 

順序分析代碼 :首先看 ExampleApp.html文件中所實現的按鈕事件,兩個按鈕事件分別是bridge.send() 方法和bridge.callHandle()方法 。在 WebViewJavascriptBridge.js 這個文件中能夠看到web

如上圖,能夠知道callHandler 和 send 方法最終都是調用了doSend 方法。分析下這個方法,變量callbackId是個字符串,responseCallBacks[] 一看就知道是個字典 ,這個字典把回調的方法responseCallback給保存起來,這Key(也就是callbackId)是惟一的,經過計數和時間應該知道這個字符串應該是惟一的,message也是一個字典,這是給message添加了一個新的key-value。幹嗎呢?我也不知道,咱們來看看sendMessageQueue是什麼,你們一看push就知道應該是個數組。他把一個字典放到一個消息隊列中(數組隊列),而後產生一個src(url scheme)。

不瞭解js的同窗(例如我)只須要知道 改變了iframe的src以後,uiwebview 會執行下邊的方法編程

 
                                         UIWebView的回調方法

因此咱們就能夠關注WebViewJavascriptBridge.m文件中的這個方法中作了些什麼事數組

 
                                           WebViewJavascriptBridge.m

這裏通過判斷以後會走到 223 行代碼,後邊return NO的緣由是:咱們要執行的是oc的代碼了,因此返回NO來阻斷 js 代碼。這裏執行了一段js代碼,點進去-(void)webViewJavascriptFetchQueyCommand方法,發現執行的js語句 WebViewJavascriptBridge._fetchQueue();而後去WebViewJavascriptBridge.js中找到這個方法。數據結構

                                        WebViewJavascriptBridge.js

返回一個字典,就是咱們在最初要發送消息時存儲起來的字典。如今咱們把要傳遞的數據拿出來,裏面存儲的東西有:函數式編程

handlerName:handlerName,

data:data,

callbackId:callbackId

拿到字典繼續向下執行下邊這個方法

 
                                       WebViewJavascriptBridgeBase.m

整體來看 就是對咱們拿出來的數據進行一系列的類型驗證(_log方法是打印數據信息的 能夠忽略)回顧前面的代碼,咱們就應該知道這裏的responseId爲空,因此執行86 -- 114行的代碼

 
                                 WebViewJavascriptBridgeBase.m

這部分是重點,到底他是怎麼要調用本地function的,callbackId你們熟悉吧,判斷是否爲空,不爲空給他指定一個block,這個不說了,block指定,此時不調用(手動調用纔會執行),這個剛纔說了用來處理native的function處理的result用於把處理後的值返回給js的,接着往下去,看到handler這個方法會從message找到handlerName,這裏咱們看一下多了一個_messageHandlers字典,從這個字典獲取一個block(WVJBHandler是一個block),直接執行了。那咱們看看_messageHandlers是怎麼被添加block的:

 
                          WebViewJavascriptBridge.m

那又是誰調用了這個方法:(在文件 ExampleAppViewController.m的viewdidload中),這裏有方法testObjecCallback

 
                        ExampleUIWebViewController

剛纔都是倒推的,若是咱們反過來,首先確定是viewdidload初始化,初始化以後會把這個block加入到_messageHandlers的數組中,以後由於js調用動態讀取這個block調用,在調用以前,咱們又把一個block付值給回掉處理的responseCallback的block,這個block在handler被調用時而被調用, 略微有點繞。如今咱們看一下這個responseCallback怎麼賦值的  

 
                        WebViewJavascriptBridgeBase.m

順着方法往下看  執行到下邊這個方法

 
                     WebViewJavascriptBridgeBase.m

對傳進來的數據   @{ @"responseId":callbackId, @"responseData":responseData };處理以後 再執行 js語句 @"WebViewJavascriptBridge._handleMessageFromObjC('%@');"

看看js的方法  

 
                                   WebViewJavascriptBridge.js

這個裏面應該很容易看到  代碼進入待66 行   由於傳進來的數據中responseId 顯然不爲空  而這裏面的responseCallback 方法  和responseCallbacks 數組又是何處來的呢?你們可能已經忘了,回到文首doSend方法(返回去看看),除了包裝一個message 字典存起來,還有把 responseCallback 存在了responseCallBacks[]中,因此等到原生的方法執行完以後再調用這個方法(在最初 button點擊事件裏面已經實現了 )實現相互通訊。

這裏稍微總結下,便於理解

1.首先是在UIWebViewController 裏面實例化 一個bridge,經過bridge 註冊一個 handler,而後保存在messageHandlers中  

2. 點擊網頁的button的時候,把信息保存起來生成一個message字典三個key(handlerName , data,callbackId(後邊經過這個來找到以前的responseCallback方法))   而且把 其中的responseCallback保存起來,而且改變iframe.src  

3. 這個時候webView執行代理方法,在這裏面取出2步存起來的信息,而後給1步的handler中的responseCallback賦值,而且執行 1步註冊的方法。因此結果就是執行oc的回調方法,而後在oc的回調方法裏面再去執行,剛剛被賦值的 responseCallback方法(這個方法的響應結果體如今web中),至於這個responseCallback被賦值的過程就是經過第二步的callbackId 找到相應的方法賦過去。

二. native調用js

過程不是直接調用js,跟經過js調用Native的處理方式是同樣的。能夠看到,最後調用的就是WebViewJavascriptBridgeBase中的這個方法  

- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName; 
 
                                  WebViewJavascriptBridgeBase.m

把data、handlerName、callbackId判空而且存在message, 並且把resopnseCallback方法存起來。而後,後邊同上文同樣,最後執行到JS的這個方法

 
                              WebViewJavascriptBridge.js

不過此次執行的是 71 - 90 行的代碼,兩個判斷  

1  若是有callbakId存在,那麼就給實現responseCallback這個方法(並不調用)  

2  若是message.handlerName存在,那麼就取出messageHandlers中 message.handlerName 對應的方法,這個方法通常是在js代碼中註冊過的

 
                                  ExampleApp.html

這裏最後,若是既有handler又有callback,就會把第一步實現的方法賦值給handler的responseCallback,而後在執行到handler的最後一句  responseCallback(responseData)時候,再執行這個回調。

三.  使用  

通常來講,都不會這麼複雜的傳輸數據。通常只須要單向的去傳遞數據,不會有不少的callback 來回的調用。至於oc 的初始化和html的初始化,對照github上面的demo進行就能夠了 。      

大多數狀況下都是js在調用native的方法,因此通常都是咱們在方法中做以下的工做

[_eBridge registerHandler:@"backToHomeHandle" handler:^(id data,   WVJBResponseCallback responseCallback) {

        [weakSelf.navigationController popToRootViewControllerAnimated:YES];

}];

這裏 @"backToHomeHandle" 就是約定的方法名  block回調中就是當js代碼調用該方法時 咱們的原生界面要作出的響應。而後 和 前端的同窗 約定好 數據結構   讓他們在適當的時候 調用咱們的方法便可。工做中用到了 學習了下 供你們參考。能夠領略一下這個庫裏面對block回調 以及 js函數式編程的運用。

相關文章
相關標籤/搜索