UIWebView中JS與OC交互 WebViewJavascriptBridge的使用
一、綜述
現在很多的應用都會在多種平臺上發布,所以很多程序猿們都開始使用Hybrid App的設計模式。就是在app上嵌入網頁,只要寫一份網頁代碼,就可以跑在不同的系統上。在iOS中,app多是通過WebView來加載網頁,由於功能需求等原因,代碼中少不得要和跟網頁交互。
二、原理
在iOS中,本地調用Javascript語言,是通過UIWebView中的實例方法stringByEvaluatingJavaScriptFromString:來實現的,該方法通過字符串對象的形式傳入JS代碼。
1 |
[webView stringByEvaluatingJavaScriptFromString:@ "Math.random();" ];
|
而JS調用本地的代碼,則並沒有現成的API,而是需要間接地通過一些方法來實現。我們利用UIWebView的代理方法,當UIWebView發起的所有網絡請求,都可以通過delegate函數在Native層得到通知。這樣,我們就可以在UIWebView內發起一個自定義的網絡請求,比如:‘wvjbscheme://__BRIDGE_LOADED__‘。於是在UIWebView的delegate函數中,我們攔截url,只要發現是我們自定義的url,就不進行內容的加載,轉而執行相應的調用邏輯。
三、WebViewJavascriptBridge的使用
1、WebViewJavascriptBridge簡介
WebViewJavascriptBridge支持到iOS6之前的版本的,用於支持native的iOS與javascript交互,接下來講講WebViewJavascriptBridge的基本原理及應該如何去使用,包括iOS端的使用和JS端的使用。
首先,看看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被識別為未知的消息。
2、WebViewJavascriptBridge的使用
首先,要在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調用
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
UIWebView中JS與OC交互 WebViewJavascriptBridge的使用