(一)通過URL傳遞函數(shù)名稱和參數(shù)
這個方案是歷史最悠久,至少目前也是使用最普遍的方案。這個方案的優(yōu)點(diǎn)是技術(shù)最簡單,通過scheme://host?query的形式由url來傳遞,iOS和Android都可以用最基礎(chǔ)的方式來實(shí)現(xiàn)俭尖。
當(dāng)然這里還是有一個比較有名的第三方庫WebViewJavascriptBridge不過用這個庫,需要JS洞翩、iOS稽犁、Android三方要協(xié)調(diào)好,最好都要用它骚亿。最后的使用接口還是簡單清晰的已亥。同時支持UIWebView和WKWebView,這種與時俱進(jìn)的做法還是值得提倡来屠。
Native調(diào)JS
UIWebView
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
WKWebView
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;
WKWebView需要添加WebKit.framework陷猫,最低支持版本iOS8秫舌,Xib不能用,只能代碼寫界面。
條件允許的情況下指攒,建議用WKWebView米罚,系統(tǒng)性能的提升是硬道理。
JS調(diào)Native
UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
WKWebView
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
NSURLRequest中有NSURL參數(shù)嘱支,以格式scheme://host?query約定好,JS告訴Native想要達(dá)到的目的,Native根據(jù)參數(shù)完成相應(yīng)的功能零抬。
JS和iOS、Android的寫法需要相互配合宽涌,才能完成相應(yīng)的通訊過程平夜。
JS調(diào)用Native大致步驟
Step1:Native通過截取的url,將scheme://host?key1=value1&key2=value2格式解析出來卸亮。一般根據(jù)scheme判斷是否目標(biāo)JS忽妒,決定是否處理;host部分可以約定為模塊名或者類名或者函數(shù)名等兼贸;問號后面的key1=value1&key2=value2就是url的query部分段直,可以放參數(shù)名;當(dāng)然也可以host部分不用溶诞,將所有信息都放入后面的參數(shù)中鸯檬。
Step2: Native執(zhí)行一段事先定義的固定的JS代碼,告訴JS已經(jīng)正確收到命令螺垢。比如:
[self.webView evaluateJavaScript:@"bridge.nativeCallComplete()" completionHandler:nil];
Step3:JS端收到命令響應(yīng)之后等待或者做其他事情喧务,Native端根據(jù)解析出來的參數(shù)調(diào)用本地模塊完成響應(yīng)的功能
Step4:Native執(zhí)行完畢后,執(zhí)行一段事先定義的固定的JS代碼枉圃,告訴JS執(zhí)行結(jié)果功茴。
比如:
[self.webView evaluateJavaScript:@"[self.webView evaluateJavaScript:@"javascript:bridge.invokeJs('result=3')" completionHandler:nil];" completionHandler:nil];
- Step5:JS端根據(jù)收到的結(jié)果,決定下一步的響應(yīng)讯蒲。
參考文章
UIWebView與JavaScript(JS) 回調(diào)交互
WebViewJavascriptBridge簡介
通過前面的介紹可以知道痊土,Native調(diào)用JS和JS調(diào)用Native是很不一樣的。并且在JS調(diào)用Native的過程中墨林,有好幾次用到Native調(diào)用JS的過程(告訴JS命令接收赁酝,告訴JS處理結(jié)果)。估計(jì)這也是讓人感覺復(fù)雜的地方旭等。
WebViewJavascriptBridge的設(shè)計(jì)比較巧妙酌呆,經(jīng)過包裝,JS和Native互相調(diào)用的寫法完全一致搔耕,實(shí)現(xiàn)了“全雙工通訊”隙袁。
marcuswestin/WebViewJavascriptBridge
通訊函數(shù)
typedef void (^WVJBResponseCallback)(id responseData);
typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback);
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
在一端用registerHandler
進(jìn)行注冊痰娱,在另一端就可以用callHandler
進(jìn)行調(diào)用,調(diào)用結(jié)果通過Block以異步的方式告知菩收。從調(diào)用形式上看梨睁,Native和JS完全一樣,使用起來非常方便娜饵。
Native端注意點(diǎn)
+ (instancetype)bridgeForWebView:(WKWebView*)webView;
- (void)setWebViewDelegate:(id<WKNavigationDelegate>)webViewDelegate;
第一個函數(shù)將Bridge和某個webView建立聯(lián)系
第二個函數(shù)設(shè)置webView的代理(外部代理)坡贺,在函數(shù)內(nèi)部,Bridge是真正的代理箱舞,進(jìn)行截取url操作之后遍坟,Bridge會調(diào)用在這里設(shè)的這個代理,讓外部代理起作用晴股。代理模式中WebView的代理只有一個愿伴,就是Bridge,通過“二傳手”模式电湘,從外面看起來好像WebView有兩個代理一樣隔节。
如果是UIWebView,使用的類是
WebViewJavascriptBridge
如果是WKWebView胡桨,使用的類是
WKWebViewJavascriptBridge
兩者的接口函數(shù)幾乎是一樣的官帘,以上兩個類基本上也是“二傳手”,只是一個容器昧谊,真正的工作在類
WebViewJavascriptBridgeBase
中完成刽虹。Android的代碼也是有的,不過是另外的第三方庫呢诬,是為了配合這個庫而寫的Android版本涌哲。這樣JS、iOS尚镰、Android可以良好地配合起來阀圾。
jesse01/WebViewJavascriptBridge
JS端注意點(diǎn)
- JS端有固定的寫法,里面有一個例子狗唉,“ExampleApp.html”這里面有現(xiàn)成的代碼初烘,自己只要往里面填自定義的函數(shù)就可以。
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)
}
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) }
}
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
document.body.appendChild(document.createElement('br'))
var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
callbackButton.innerHTML = 'Fire testObjcCallback'
callbackButton.onclick = function(e) {
e.preventDefault()
log('JS calling handler "testObjcCallback"')
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('JS got response', response)
})
}
})
(二)通過JavaScriptCore.framework
需要引入系統(tǒng)庫JavaScriptCore.framework
最低支持iOS7
有見到過用的場所分俯,但是不多
對于iOS和Android兩個平臺肾筐,JS端是否能夠統(tǒng)一,存在不確定性
關(guān)于iOS7里的JavaScriptCore framework
JavaScriptCore框架
(三)通過WKWebView的WKUserContentController
最低支持iOS8
需要引入庫WebKit.framework
這是采用注入缸剪,代理監(jiān)聽吗铐,發(fā)消息等方式實(shí)現(xiàn)的
JS端無法統(tǒng)一iOS和Android兩個平臺的處理
WKWebView與Js實(shí)戰(zhàn)(OC版)
如何選擇
采用WebViewJavascriptBridge目前來看應(yīng)該是首選,iOS杏节、Android唬渗、JS三個平臺配合都考慮到了典阵,通信方式也比較簡單
最低支持iOS8,WKWebView的注入方式是值得考慮的镊逝。只是目前還沒有好的第三方庫支持壮啊。JS需要對iOS和Android需要分別處理,因?yàn)閮烧叩淖⑷敕绞讲煌0搿2贿^這個方向是值得嘗試的他巨,功能強(qiáng)大。
不采用WebViewJavascriptBridge减江,利用截取url的方式是目前大多數(shù)的方案,也是值得考慮的捻爷。JS辈灼、Android、iOS三者要彼此協(xié)調(diào)好也榄。
JavaScriptCore在iOS8之前由于注入的方式還是有吸引力的巡莹。不過WKWebView也提供了注入的方式,目前還看不出有什么優(yōu)勢甜紫。當(dāng)前降宅,也是使用者比較少的一種方式。