如今不少的应用都会在多种平台上发布,因此不少程序猿们都开始使用Hybrid App的设计模式。就是在app上嵌入网页,只要写一份网页代码,就能够跑在不一样的系统上。在iOS中,app可能是经过WebView来加载网页,因为功能需求等缘由,代码中少不得要和跟网页交互。javascript
在iOS中,本地调用Javascript语言,是经过UIWebView中的实例方法stringByEvaluatingJavaScriptFromString:来实现的,该方法经过字符串对象的形式传入JS代码。java
1
|
[webView stringByEvaluatingJavaScriptFromString:@
"Math.random();"
];
|
而JS调用本地的代码,则并无现成的API,而是须要间接地经过一些方法来实现。咱们利用UIWebView的代理方法,当UIWebView发起的全部网络请求,均可以经过delegate函数在Native层获得通知。这样,咱们就能够在UIWebView内发起一个自定义的网络请求,好比:'wvjbscheme://__BRIDGE_LOADED__'。因而在UIWebView的delegate函数中,咱们拦截url,只要发现是咱们自定义的url,就不进行内容的加载,转而执行相应的调用逻辑。git
一、WebViewJavascriptBridge简介github
WebViewJavascriptBridge支持到iOS6以前的版本的,用于支持native的iOS与javascript交互,接下来说讲WebViewJavascriptBridge的基本原理及应该如何去使用,包括iOS端的使用和JS端的使用。web
首先,看看WebViewJavascriptBridge.m中Webview代理拦截的代码:设计模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- (
void
)webView:(WebView *)webView decidePolicyForNavigationAction:(
NSDictionary
*)actionInformation request:(
NSURLRequest
*)request frame:(WebFrame *)frame decisionListener:(
id
<WebPolicyDecisionListener>)listener
{
if
(webView != _webView) {
return
; }
NSURL
*url = [request URL];
if
([_base isCorrectProcotocolScheme:url]) {
if
([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
}
else
if
([_base isQueueMessageURL:url]) {
NSString
*messageQueueString = [
self
_evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
}
else
{
[_base logUnkownMessage:url];
}
[listener ignore];
}
else
if
(_webViewDelegate && [_webViewDelegate respondsToSelector:
@selector
(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
}
else
{
[listener use];
}
}
|
WebViewJavascriptBridge是经过webview的代理拦截scheme,而后注入相应的JS,在拦截后,经过先经过-isBridgeLoadedURL:方法判断URL是不是须要bridge的URL,如果,则经过injectJavascriptFile方法注入JS;不然判断URL是不是队列消息,如果,则执行查询命令JS并刷新消息队列;若是都不匹配,URL被识别为未知的消息。网络
二、WebViewJavascriptBridge的使用app
首先,要在JS中接入这个框架,这段代码是不变的框架
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/**
* 此为js接入框架的函数
*/
function setupWebViewJavascriptBridge(callback) {
if
(window.WebViewJavascriptBridge) {
return
callback(WebViewJavascriptBridge); }
if
(window.WVJBCallbacks) {
return
window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement(
'iframe'
);
WVJBIframe.style.display =
'none'
;
WVJBIframe.src =
'wvjbscheme://__BRIDGE_LOADED__'
;
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
|
而后OC要调用到的JS函数要在下面函数中使用bridge.registerHandler来注册,并且JS须要调用的OC方法也要在下面的函数中用bridge.callHandler调用dom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
/**
* OC调用的JS函数需在此处注册,调用OC方法也须要在此处调用
*/
setupWebViewJavascriptBridge(function(bridge) {
var uniqueId = 1
function log(message, data) {
var log = document.getElementById(
'log'
)
var el = document.createElement(
'div'
)
el.className =
'logLine'
el.innerHTML = uniqueId++ +
'. '
+ message +
':<br/>'
+ JSON.stringify(data)
if
(log.children.length) { log.insertBefore(el, log.children[0]) }
else
{ log.appendChild(el) }
}
//注册一个给OC调用的函数,不带参数
bridge.registerHandler(
'WebViewDidLoad'
,function() {
log(
"WebViewDidLoad"
)
})
//注册一一个给OC调用的函数,接受OC传来的一个参数和一个回调处理
bridge.registerHandler(
'OC_Call_JS'
, function(data, responseCallback) {
log(
'oc调用js -'
, data)
var responseData = {
'Javascript response'
:
'oc调用JS成功!'
}
log(
'js被调用后响应-'
, responseData)
responseCallback(responseData)
})
document.body.appendChild(document.createElement(
'br'
))
var callbackButton = document.getElementById(
'buttons'
).appendChild(document.createElement(
'button'
))
callbackButton.innerHTML =
'JS_Call_ObjC'
callbackButton.onclick = function(e) {
e.preventDefault()
log(
'JS call OC'
)
//此处调用OC方法
bridge.callHandler(
'JS_Call_ObjC'
, {
'foo'
:
'bar'
}, function(response) {
log(
'JS call OC sucess and get OC rsp'
, response)
})
}
})
|
须要注意的是:在setupWebViewJavascriptBridge(function(bridge) {}函数体内的代码不能有错误,否则会致使不任何回调,不打印日志(JS的是脚本语言,跑到错的地方就不跑了)。
OC部分,首先打开框架的日志系统,而后关联webView
1
2
3
|
[WebViewJavascriptBridge enableLogging];
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
|
JS须要调用的OC方法,要在OC代码中注册
1
2
3
4
|
[_bridge registerHandler:@
"JS_Call_ObjC"
handler:^(
id
data, WVJBResponseCallback responseCallback) {
NSLog
(@
"JS调用OC: %@"
, data);
responseCallback(@
"OC被调用后响应:调用成功!"
);
}];
|
而想要调用JS中注册过的函数,在须要的地方用实例方法callHandler调用就能够了
1
2
3
4
|
id
data = @{ @
"OC调用JS"
: @
"Hi there, JS!"
};
[_bridge callHandler:@
"OC_Call_JS"
data:data responseCallback:^(
id
response) {
NSLog
(@
"testJavascriptHandler responded: %@"
, response);
}];
|
最近由于项目须要,正在边学边作Hybrid App,恰好用到这个第三方,就写了篇文分享出来,但愿能帮到刚刚入手的人,以上实例的demo地址https://github.com/GarenChen/WebViewJSBridgeDemo喜欢的顺手给个star ^_^;
推荐一些阅读:
JSBridge——Web与Native交互之iOS篇:http://www.jianshu.com/p/9fd80b785de1
Hybrid App 开发模式:http://www.tuicool.com/articles/riE3Yn
WebViewJavascriptBridge:https://github.com/marcuswestin/WebViewJavascriptBridge