从两个方面来说:前端
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;
咱们从 github上面的demo https://github.com/marcuswestin/WebViewJavascriptBridgejava
来分析一下这个库是如何实现js交互的。git
在概述中说过,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
不了解js的同窗(例如我)只须要知道 改变了iframe的src以后,uiwebview 会执行下边的方法编程
因此咱们就能够关注WebViewJavascriptBridge.m文件中的这个方法中作了些什么事数组
这里通过判断以后会走到 223 行代码,后边return NO的缘由是:咱们要执行的是oc的代码了,因此返回NO来阻断 js 代码。这里执行了一段js代码,点进去-(void)webViewJavascriptFetchQueyCommand方法,发现执行的js语句 WebViewJavascriptBridge._fetchQueue();而后去WebViewJavascriptBridge.js中找到这个方法。数据结构
返回一个字典,就是咱们在最初要发送消息时存储起来的字典。如今咱们把要传递的数据拿出来,里面存储的东西有:函数式编程
handlerName:handlerName,
data:data,
callbackId:callbackId
拿到字典继续向下执行下边这个方法
整体来看 就是对咱们拿出来的数据进行一系列的类型验证(_log方法是打印数据信息的 能够忽略)回顾前面的代码,咱们就应该知道这里的responseId为空,因此执行86 -- 114行的代码
这部分是重点,到底他是怎么要调用本地function的,callbackId你们熟悉吧,判断是否为空,不为空给他指定一个block,这个不说了,block指定,此时不调用(手动调用才会执行),这个刚才说了用来处理native的function处理的result用于把处理后的值返回给js的,接着往下去,看到handler这个方法会从message找到handlerName,这里咱们看一下多了一个_messageHandlers字典,从这个字典获取一个block(WVJBHandler是一个block),直接执行了。那咱们看看_messageHandlers是怎么被添加block的:
那又是谁调用了这个方法:(在文件 ExampleAppViewController.m的viewdidload中),这里有方法testObjecCallback
刚才都是倒推的,若是咱们反过来,首先确定是viewdidload初始化,初始化以后会把这个block加入到_messageHandlers的数组中,以后由于js调用动态读取这个block调用,在调用以前,咱们又把一个block付值给回掉处理的responseCallback的block,这个block在handler被调用时而被调用, 略微有点绕。如今咱们看一下这个responseCallback怎么赋值的
顺着方法往下看 执行到下边这个方法
对传进来的数据 @{ @"responseId":callbackId, @"responseData":responseData };处理以后 再执行 js语句 @"WebViewJavascriptBridge._handleMessageFromObjC('%@');"
看看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 找到相应的方法赋过去。
过程不是直接调用js,跟经过js调用Native的处理方式是同样的。能够看到,最后调用的就是WebViewJavascriptBridgeBase中的这个方法
- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName;
把data、handlerName、callbackId判空而且存在message, 并且把resopnseCallback方法存起来。而后,后边同上文同样,最后执行到JS的这个方法
不过此次执行的是 71 - 90 行的代码,两个判断
1 若是有callbakId存在,那么就给实现responseCallback这个方法(并不调用)
2 若是message.handlerName存在,那么就取出messageHandlers中 message.handlerName 对应的方法,这个方法通常是在js代码中注册过的
这里最后,若是既有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函数式编程的运用。