文字较多,耐心读一下,肯定有所收益!
*开发中经常使用WebViewJavascripBridge进行H5与原生的交互,也知道OC用代理拦截H5的url做出一些操作,OC可以注入JS代码改变H5的样式什么的,但一直困惑的我的是:
- 他们是怎么把我OC端的一个NSDictionary、NSArray、NSString的数据发给H5端的,又怎么把H5端的数据发给我的呢 ????????????
- 最后明白了【数据通道】 :
- webview注入js代码的API:evaluateJavaScript: completionHandler:^(id _Nullable result, NSError * _Nullable error){ }
两端 数据传递 通道
//1. OC端需要注入【可执行的JS代码】的字符串: @"fuction('data')",OC端的可以将数据以JS方法fuction的参数 data 传入,在fuction中将数据data取出以供HTML端使用。
[webView evaluateJavaScript:@"fuction('data')" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
//2. result 是执行完JS代码后返回的结果,HTML端的数据可以通过result返回到OC端以供使用,即执行fuction方法后,return一个数据交给OC端。
//3. error 是执行完JS代码时遇到的error,可忽略。
}];
两端的数据传递具体过程
总结性的讲:
- OC端发数据是组装好数据直接发;
- HTML端发数据是组装好数据,通知OC端自己过来取数据。
- OC端发送数据:
1. 准备数据:创建一个字典,以key-Value的形式组装成一个data,然后将data序列化成一个转义后的json字符串;
2. 利用注入JS代码通道:[webView evaluateJavaScript:@"sendData('data')" completionHandler:^(id _Nullable result, NSError * _Nullable error) { }];
直接将data送入JS代码中,并在sendData方法中将data取出,以供HTML页面使用处理逻辑什么的。
- JS端发送数据:
1. 准备数据:js创建一个对象,以key-value的形式组装一个data,然后将data序列化成json字符串;
2. 利用一个<i>标签,重设一下改标签的src(该src是两端约定好的),发起一个请求;
3. OC端通过WebView的代理方法拦截到了url,判断一下url是否是之前约定好的HTML发送数据的url,如果是则组织一段JS代码字符串: @"getData()";
4. OC端利用注入JS代码通道:[webView evaluateJavaScript:@"getData()" completionHandler:^(id _Nullable result, NSError * _Nullable error) { // result :HTML端返回的数据 }];
JS代码中执行getData()方法,在该方法中JS将组装好的data,return出来。
5. 然后 OC 端再从Block中获取到HTML端发送的数据data,从data中取出数据处理相关逻辑。
知道了JS与OC之间的数据传输通道,以及传输的具体过程后,我们再看这个框架。发现两端创建的bridge,其实就是在为这个数据传输通道做辅助工作,比如js端定义了两个方法:function _fetchQueue()、function _handleMessageFromObjC( data ),前一个方法是OC获取JS端数据时注入js代码字符串执行该方法,该方法将js端的数据return出来;后一个方法是OC将数据传送JS端时,将OC端数据作为该方法的参数,注入js代码字符串执行该方法,完成了将数据传给JS操作。因此可见,bridge就是为这条数据通道做辅助工作的。
就是因为没搞明白这条结论,才导致网络上介绍WebViewJavascripBridge框架的文章,将重点放在bridge上讲,讲bridge是如何如何执行这个方法,那个方法,把代码流程讲了一遍,看似讲明白了,但真的是隔靴搔痒,未讲到核心点。一开始我也是这样,惭愧!
- 有了以上的认识之后,我们再看bridge。我们都知道两端都一个bridge,刚才也说了bridge就是为数据通道服务的,那么bridge都能帮数据通道做什么呢?有通过哪些东西辅助呢?
只要介绍bridge提供的辅助工具就能全部理解了:
一个属性、 两张表、 四个方法。
*一个属性:sendMessageQueue,是一个数组类型,用于存储该端组装的Message,即Message的集合。这里的Message就是一个一个的数据包,是key-Vlaue的对象。为什么是用数组呢?个人认为保证数据传递顺序。
*两张表:方法表:保存的是该端对外注册的方法以供一端发送消息调用,以key-value的形式保存,比如:“买苹果”:{ 买苹果的具体操作函数 }。
回调表:保存的是发送消息让另一端执行后的回调,也是以key-value的形式保存,其key是按照一定规则创建保证其唯一性,比如:"买手表的回调":{把表买回来之后的操作函数}
- 四个方法:1. 注册方法:两端约定一个key,将key对应执行的方法保存到表。
2 . 发消息方法:使用约定好的key,在带上数据data,发送消息让另一端执行相关操作,如果需要执行完成后给出回调,则在发送消息时也把回调处理的block带上,那这个block会存在回调表。
3 . 组装数据方法:将发送消息方法中提供的key、data、block的key组装成功一个Message,并转成json字符串,以供发送给另一端使用。
4 . 解析数据方法:将另一端发送到数据,从json字符串转成一个key-value对象,根据key,从方法表中找到方法,并将data最为该方法的参数,然后执行该方法,执行完成后,如果从message中能获取到block的key,则将block的key和执行后的结果数据,在发送消息给另一端,另一端获取到消息后,从回调表中根据key找到block,然后将回到来的数据作为block参数,执行回调block。
例子:OC端让H5端买手表
1. H5端在事件表注册事件:“买手表” :{...}
2. OC端在回调表中保存买表的回调:"callbackID" : {买表后的回调}
3. OC端创建了一个Message:{handleName:"买手表", data : "1000元", responseCallback : "callbackID"},保存在sendMessageQueue中
4. OC端通过【组装数据方法】将message从sendMessageQueue中取出转成了一个json字符串,并将该数据以参数的形式放入拼接的JS代码字符串中,这段JS代码就是执行JS端【解析数据方法】
5. OC端通过数据通道将数据传入JS端
6. JS端执行【解析数据方法】,在方法中将参数,从json字符串转成key-value对象,得到handleNmae、data、callBackID。首先根据handleNmae从注册表中找到买表的函数,然后将data中的【1000元】作为参数执行买手表的{...买手表去喽...}
7. JS端买完手表后,需要将手表返回给OC端,这时JS端将callbackID和表组成了一个Message:{responsID:"callbackID" , data : 表 },保存到sendMessage中
8. JS端通过一个i标签,改变src发起一个请求,通知OC端过来拿数据
9. OC端拦截到url,得知要他去取数据,则组织了一段JS代码,通过数据通道将数据拿回来,即调用JS端的【组装数据方法】将从sendMessageQueue中拿到Message返回到OC端
10. OC端拿到数据后,获取callbackID,并根据它从回调表中找到回调的block,取出data:表,将表作为block的参数执行block。这时OC已经拿到了他的表了,到此结束。