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函数式编程的运用。

相关文章
相关标签/搜索