如今不少的應用都會在多種平臺上發佈,因此不少程序猿們都開始使用Hybrid App的設計模式。就是在app上嵌入網頁,只要寫一份網頁代碼,就能夠跑在不一樣的系統上。在iOS中,app可能是經過WebView來加載網頁,因爲功能需求等緣由,代碼中少不得要和跟網頁交互。javascript
在iOS中,本地調用Javascript語言,是經過UIWebView中的實例方法stringByEvaluatingJavaScriptFromString:來實現的,該方法經過字符串對象的形式傳入JS代碼。java
[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代理攔截的代碼:設計模式
- (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中接入這個框架,這段代碼是不變的框架
/** * 此爲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
/** * 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
[WebViewJavascriptBridge enableLogging]; _bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
JS須要調用的OC方法,要在OC代碼中註冊
[_bridge registerHandler:@"JS_Call_ObjC" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"JS調用OC: %@", data); responseCallback(@"OC被調用後響應:調用成功!"); }];
而想要調用JS中註冊過的函數,在須要的地方用實例方法callHandler調用就能夠了
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