IOS WebViewJavascriptBridge 使用以及原理分析


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

 

概述

從兩個方面來講:

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

我們從 github上面的demo https://github.com/marcuswestin/WebViewJavascriptBridge

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

詳解

一.  js調用native方法

在概述中說過,js是不能直接調用native的method所以,需要借助- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType這個方法。這個方法大家不陌生,每次在重新定向URL的時候,這個方法就會被觸發,通常情況,我們會在這里做一些攔截,用來完成js和本地的間接交互。那么WebViewJavascriptBridge也不另外,也是這么做。

 

 
 

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

如上圖,可以知道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函數式編程的運用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM