WebViewJavascriptBridge使用笔记

(文章转载自:http://zhangbuhuai.com/2015/06/16/using-WebViewJavascriptBridge/)

WebViewJavascriptBridge使用笔记

 2015-06-16 更新日期:2015-06-25

文章目录
  1. 1. 使用WebViewJavascriptBridge
    1. 1.1. 初始化
      1. 1.1.1. OC初始化
      2. 1.1.2. JS初始化
    2. 1.2. 发送消息
      1. 1.2.1. OC向JS发送消息
      2. 1.2.2. JS向OC发送消息
      3. 1.2.3. 理解responseCallback
    3. 1.3. OC和JS互相调用
      1. 1.3.1. OC定义和调用JS handler
      2. 1.3.2. JS定义和调用OC handler

写在前面

在App中使用Web代替一些Native UI已经成为移动客户端开发的一种思潮。在App中嵌入Web有一个重要的基础问题:Objective-C和JavaScript的交互。

近期的项目需要,笔者开始着手这方面的问题学习。很容易想到:一定存在某个引擎能够在OC和JS之间转换。去github中搜索关键字iOS JavaScript得到的选择并不多,第三方库WebViewJavascriptBridge的Stars遥遥领先,自然选择它作为OC和JS的交互引擎了。

本文是笔者了解WebViewJavascriptBridge过程中的一些学习记录,好记性不如烂笔头嘛!

使用WebViewJavascriptBridge

WebViewJavascriptBridge的使用不难,WebViewJavascriptBridge提供的Example比较直观的展示了各种接口的使用。

App中嵌入Web一般需要使用UIWebView(除非你自己写一个),WebViewJavascriptBridge正是配合UIWebView进行工作的。

初始化

和其他第三库一样,使用WebViewJavascriptBridge需要做一些初始化,只是WebViewJavascriptBridge的初始化包括两部分:「Objective-C初始化」和「JavaScript初始化」。

OC初始化

OC中初始化WebViewJavascriptBridge的前提是存在一个UIWebView对象,每个WebViewJavascriptBridge对象都应该与一个UIWebView对象绑定。

// 创建UIWebView对象
UIWebView * webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
 
// 配置logging
[WebViewJavascriptBridge enableLogging];
 
// 创建WebViewJavascriptBridge对象并与UIWebView对象绑定
self.bridge = ({
WebViewJavascriptBridge* bridge =
[WebViewJavascriptBridge bridgeForWebView:webView
webViewDelegate:self
handler:^(id data, WVJBResponseCallback responseCallback) {
// do something
// responseCallback(responseData)
}
];
bridge;
});

使用类方法创建一个WebViewJavascriptBridge对象,其中有一个block类型的handler。这个handler用来处理来自于JavaScript发送的消息,handler的形参与JavaScript中的send方法的形参对应,一般有两个参数,第一个参数是JS send传入的参数(可以是任意类类型),第二个是JS send传入的回调handler。

P.S:看客可能像我当初一样不太理解第二个参数:responseCallback,后文会对此进行详细说明。

JS初始化

除了OC外,JS中也得执行针对WebViewJavascriptBridge的初始化代码。这意味着,除了客户端(iOS开发人员)外,服务端(后端写JS的开发人员)也得对WebViewJavascriptBridge有所了解。好在需要理解的内容不多,十分钟就可以搞定。JavaScript对WebViewJavascriptBridge初始化过程(这部分代码几乎是固定的)如下:

// 创建了一个connectWebViewJavascriptBridge方法,该方法名是固定的
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener('WebViewJavascriptBridgeReady', function() {
callback(WebViewJavascriptBridge)
}, false)
}
}
 
// 调用connectWebViewJavascriptBridge方法
connectWebViewJavascriptBridge(function(bridge) {
bridge.init(function(message, responseCallback) {
// do something
responseCallback(responseData)
})
})

先创建了一个connectWebViewJavascriptBridge方法,该方法注册了一个WebViewJavascriptBridgeReady事件,同时声明了一个全局的WebViewJavascriptBridge变量,这样我们可以在外部通过WebViewJavascriptBridge调用相关方法。

bridge.init里面同样定义了一个匿名function,这个function用来接收Objective-C里面通过send方法发送的消息的,参数与OC里的send方法参数对应。同样,一般有两个参数,第一个参数是OC send传入的参数(可以是任意类类型),第二个是OC send传入的回调handler。

可以简单总结一下。初始化的根本目的是啥?
根据我的理解,初始化的根本目的是:消息接收者定义message handler

P.S:请记住message handler这个名词,后文会经常用到。

发送消息

上述「初始化」操作的目的是为了确保OC和JS能够相互处理来自对方的消息。

除了「初始化」操作之外,WebViewJavascriptBridge对发送消息也有所约束,这意味着OC和JS发送消息必须得遵守一定的格式。

OC向JS发送消息

OC向JS发送消息,定义了两个用于「发送消息」的接口:

// APIs
- (void)send:(id)data;
- (void)send:(id)data responseCallback: (WVJBResponseCallback)responseCallback;

两个接口的区别只是参数个数不同,参数data指的是「传给JS的参数」,responseCallback参数是一个block,给JS发送消息后,JS的message handler可能会返回一些值,responseCallback就是用来处理返回值的。

P.S:data可以为空;这里的「返回值」并不是非常准确的说法,只是一种参考「函数」的说法,准确来讲应该叫response data

JS向OC发送消息

JS向OC发送消息,WebViewJavascriptBridge也定义了两种格式:

bridge.send(data)
bridge.send(data, function responseCallback(responseData) { ... })

显然,无论是「OC向JS发送消息」,还是「JS向OC发送消息」,都有两种格式,含有responseCallback和不含responseCallback

P.S:data可以为空;

理解responseCallback

上文已经多次提到了responseCallback,可能是由于笔者对跨平台了解得比较少,也可能是对函数式编程了解不多,刚开始对responseCallback不甚理解。这一小段将对responseCallback进行详细阐述。

关于「消息处理」和「消息发送」,我是参考「函数定义」和「函数调用」这两个概念来理解的。「函数定义」定义了函数的具体工作(即说明这段代码块都干了些啥),「函数调用」指示执行具体代码块;根据我的理解,从概念上讲,「消息处理」对应「函数定义」,「消息发送」对应「函数调用」。

这段话非常啰嗦,但引入这么一种对应关系是为了更好说明responseCallback

对于函数(广义上的「函数」,而不仅仅指JavaScript function)来说,函数可能有返回值,也可能没有返回值。当有返回值时,调用者往往会定义变量接收返回值,方便之后使用返回值…而上文中反复出现的responseCallback有些类似于对函数返回值的处理,消息发送方向消息接收方发出一个消息,除了希望对方处理某些事情之外,可能还期待对方返回一些数据(response data),这些数据往往会在后续的处理中起作用。

因此,若「消息接收方」的message handler中可能有需要传给「消息发送方」的response data时,「消息发送方」还需要定义一个handler用来处理这些值,即所谓的responseCallBack

P.S:为什么函数处理返回值使用ret = aFunction(variable)这样的格式,而这里使用responseCallBack处理呢?我想是因为这里处理的是两种不同语言,过程中难免存在类型转换,况且,还有可能是由于并发。

对于函数而言,若某个函数有返回值,但调用者不想要保存该返回值,此时往往不会定义变量接收该返回。这在大多属于语言中是被允许的。

对于WebViewJavascriptBridge也一样,「消息发送方」发送消息时,可以不传入responseCallback参数,表示对「消息接收方」的response data不care。

在定义message handler时,在handler的responseCallback(responseData)好比函数中return ret

P.S:在定义message handler时,并不要求一定有responseCallback(responseData)这么一句代码;只是个人觉得,有必要写上,哪怕没有任何response data需要返回,也得加上responseCallback(null)。类似于函数,若某个函数没有返回值,也没有显式调用return语句,在编译阶段,编译器也会帮助在末尾加上return void

理清了responseCallback这个概念,就基本上算是学会使用WebViewJavascriptBridge了。

OC和JS互相调用

「OC和JS互相调用」指的是OC和JS互相对应对方的handler(block或function)。

笔者刚开始觉得啰嗦:既然「消息机制」能够解决OC和JS交互问题,为啥还需要OC和JS互相调用对方的handler呢?

我还没有找到比较权威的的说法,但这里也谈谈自己的一点理解。

先说「函数」,「函数」的本质不过是代码的一种组织结构,它使得代码块具有了更好的可读性,同时极大加强代码复用。

基于「消息机制」,我们可以尽可能实现任何基于文本交互。可以做的事情非常丰富,譬如「消息发送者」传入参数1message handler执行A段代码,传入参数2,执行B段代码。但问题是:基于「消息机制」,OC和JS之间的几乎所有交互任务都得写在message handler中。当交互任务变得复杂时,代码组织将是一种灾难(会充斥很长并且嵌套很深的if语句)。以消息的第一个参数data为例,有时候,data可能是一个URL字符串,有时候可能是一个JSON字符串,有时候可能只是一个数值,光是解析这些参数,都需要一个非常复杂的if语句…写到这里,「在OC和JS中定义能被对方调用的handler」的意义就不需要多讲了。

关于「OC和JS互相调用」,WebViewJavascriptBridge也定义了一些约束。约束包括两部分:定义handler的姿势,调用handler的姿势。

OC定义和调用JS handler

所谓定义handler,其实是向bridge注册一个handler,如下:

// API
// - (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler
 
// eg:
[self.bridge registerHandler:@"OCHandlerName"
handler:^(id data, WVJBResponseCallback responseCallback) {
// do something
responseCallback(responseData);
}
];

向bridge注册handler包括两部分内容:name和handler body。

调用JS的handler也简单,如下:

// APIs
// - (void)callHandler:(NSString *)handlerName;
// - (void)callHandler:(NSString*)handlerName data:(id)data;
// - (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
 
// eg:
[self.bridge callHandler:@"JSHandlerName" data:data];

JS定义和调用OC handler

在JS定义(注册)handler的姿势如下:

bridge.registerHandler("handlerName", function(responseData) { ... })

调用OC的handler也简单,和OC调用JS handler类似。

总结

本文写得好啰嗦啊!

参考资料

posted @ 2016-01-08 17:18  我叫南部21号  阅读(324)  评论(0)    收藏  举报