WebViewJavascriptBridge 是一個封裝處理好的第三方庫.功能非常強大.下面就簡單介紹一下如何使用這個WebViewJavascriptBridge庫,以供大家交流探討和學(xué)習(xí).
在學(xué)習(xí)前 先去github 下載這個庫 ,下載: https://github.com/marcuswestin/WebViewJavascriptBridge
我們先看看這個庫是如何處理的.
WebViewJavascriptBridge如其名字定義陕赃,就相當于一座橋梁跨跨,兩端連接了Obj-C和JavaScript。它提供了OC和JS互調(diào)的方法接口,方法在互調(diào)之前芦鳍,我們需要向?qū)Ψ阶晕覀兊姆椒斜?
1. OC調(diào)用和回調(diào)JS
//1.1 JS注冊O(shè)C的方法并實現(xiàn)回調(diào)OC
bridge.registerHandler('JS EchoDemo',function(data, responseCallback) {
console.log("-----JS demo called with:", data)
responseCallback(data)
})
//1.2 OC調(diào)用JS方法并實現(xiàn)回調(diào)函數(shù)
[self.bridge callHandler:@"JS EchoDemo"responseCallback:^(id responseData) {
NSLog(@"ObjC received response+++: %@", responseData);
}];
2. JS調(diào)用和回調(diào)OC
//2.1 OC注冊JS的方法并實現(xiàn)回調(diào)JS
[self.bridge registerHandler:@"ObjC EchoDemo"handler:^(iddata, WVJBResponseCallback responseCallback) {
NSLog(@"ObjC Echo called with+++: %@", data);
responseCallback(data);
}];
//注意方法名稱一致
//2.2 JS調(diào)用OC方法并實現(xiàn)回調(diào)函數(shù)
bridge.callHandler('ObjC EchoDemo', {'key':'value'},functionresponseCallback(responseData) {
console.log("------JS received response:", responseData)
})
先看下庫的文件,一共有8個文件闪萄,而我們關(guān)心的文件只有3個誊涯,因為文件5,6是兼容WKWebView样刷,實現(xiàn)原理都是一樣的.
1. WebViewJavascriptBridge.h
2. WebViewJavascriptBridge.m
3. WebViewJavascriptBridgeBase.h
4. WebViewJavascriptBridgeBase.m
5. WKWebViewJavascriptBridge.h
6. WKWebViewJavascriptBridge.m
7. WebViewJavascriptBridge_JS.h
8. WebViewJavascriptBridge_JS.m
我們需要重點關(guān)注文件2仑扑,4,8中的代碼置鼻,值得一提的是文件7镇饮,8是 注入JS 相關(guān)的文件
實現(xiàn)部分
1. 首先我們有一個bridge對象
@property(nonatomic, strong) WebViewJavascriptBridge *bridge;
2.初始化對象
self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
bridge在初始化的時候會觸發(fā)bridgeBase的初始化(第4個文件),實際上消息的處理和回調(diào)都是在bridgeBase中完成的箕母,bridge的作用是:進行webView代理的傳遞储藐,調(diào)用bridgeBase中消息的注冊,調(diào)用和回調(diào)處理.
我們來看下bridgeBase的頭文件中的屬性嘶是,有消息隊列數(shù)組钙勃,消息處理字典,響應(yīng)回調(diào)字典和具體的消息處理對象
@property(assign)id delegate;
@property(strong,nonatomic)NSMutableArray* startupMessageQueue;
@property(strong,nonatomic)NSMutableDictionary* responseCallbacks;
@property(strong,nonatomic)NSMutableDictionary* messageHandlers;
@property(strong,nonatomic) WVJBHandler messageHandler;
3 .當webView第一次加載的時候聂喇,我們看下面的代碼辖源,首先判斷是否是當前scheme,之后判斷URL是否是第一次加載的URL希太,與消息隊列有關(guān)的URL定義與第一次加載的URL定義和處理方式是不同的克饶,所以這里要進行區(qū)分,第一次加載的時候我們會注入(第8個文件)中的相關(guān)JS代碼誊辉。消息隊列相關(guān)的URL我們需要對消息進行處理
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
if(webView != _webView) {returnYES; }
NSURL*url = [request URL];
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if([_base isCorrectProcotocolScheme:url]) {
if([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
}elseif([_base isQueueMessageURL:url]) {
NSString*messageQueueString = [self_evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
}else{
[_base logUnkownMessage:url];
}
returnNO;
}elseif(strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
return[strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
}else{
returnYES;
}
}
4 .第一次加載的時候我們會注入JS代碼彤路,執(zhí)行"_evaluateJavascript:"方法,實際調(diào)用的是webView中的"stringByEvaluatingJavaScriptFromString:"方法來注入JS代碼芥映,注入之后如果此時隊列消息有消息的話就會進行消息的調(diào)用
- (void)injectJavascriptFile
{
NSString*js = WebViewJavascriptBridge_js();
[self _evaluateJavascript:js];
if(self.startupMessageQueue) {
NSArray* queue =self.startupMessageQueue;
self.startupMessageQueue =nil;
for(idqueuedMessage in queue) {
[self_dispatchMessage:queuedMessage];
}
}
}
5 接下來我們看下JS端的部分代碼洲尊,注入的這部分JS代碼首先為html的window增加了一個bridge對象远豺,這個對象里面有具體的有消息隊列數(shù)組,消息處理字典坞嘀,響應(yīng)回調(diào)字典和具體的消息處理對象局义,和OC端的bridge是保持一致的雇盖,到這里完成了JS部分的初始化岸霹,整個初始化也就完成了
if(window.WebViewJavascriptBridge) {
return;
}
window.WebViewJavascriptBridge = {
registerHandler: registerHandler,
callHandler: callHandler,
_fetchQueue: _fetchQueue,
_handleMessageFromObjC: _handleMessageFromObjC
};
varmessagingIframe;
varsendMessageQueue = [];
varmessageHandlers = {};
6.前文提到了OC調(diào)用JS方法拄显,首先需要在JS端進行方法的注冊,我們來看方法的注冊代碼矢渊,函數(shù)定義部分是不變的继准,只要使用這個庫,那么這一部分代碼就是必須有的矮男,調(diào)用函數(shù)傳入回調(diào)部分的代碼就是我們自己需要自定義的地方移必,這里的registerHandler中的函數(shù)名就是OC要調(diào)用JS的方法名
//此部分屬于公用 ?js代碼
function setupWebViewJavascriptBridge(callback) {
if(window.WebViewJavascriptBridge) {returncallback(WebViewJavascriptBridge); }
if(window.WVJBCallbacks) {returnwindow.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
varWVJBIframe = 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) {
//1 注冊JS的方法給OC
bridge.registerHandler('JS Echo',function(data, responseCallback) {
console.log("JS Echo called with:", data)responseCallback(data)
})
})
OC調(diào)用的時候callHandler的時候
(void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
NSMutableDictionary* message = [NSMutableDictionarydictionary];
if(data) {
message[@"data"] = data;
}
if(responseCallback) {
NSString* callbackId = [NSStringstringWithFormat:@"objc_cb_%ld", ++_uniqueId];
self.responseCallbacks[callbackId] = [responseCallbackcopy];
message[@"callbackId"] = callbackId;
}
if(handlerName) {
message[@"handlerName"] = handlerName;
}
//消息派遣
[self_queueMessage:message];
}
8. 拿到對應(yīng)的消息之后,首先要格式化成字符串對象毡鉴,因為webView執(zhí)行JavaScript腳本的時候接受的是一個字符串對象崔泵,之后進行一些列的轉(zhuǎn)換處理,在下面的代碼我們注意到有這樣的字符串"WebViewJavascriptBridge._handleMessageFromObjC"猪瞬,這個就是我們之前注入的JS腳本里面對OC方法的處理的函數(shù)憎瘸,于是我們成功的把OC的消息傳遞給了JS
- (void)_dispatchMessage:(WVJBMessage*)message {
NSString*messageJSON = [self_serializeMessage:message pretty:NO];
[self_log:@"SEND"json:messageJSON];
//格式化json
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\"withString:@"\\\\"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\""withString:@"\\\""];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'"withString:@"\\\'"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n"withString:@"\\n"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r"withString:@"\\r"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f"withString:@"\\f"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028"withString:@"\\u2028"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029"withString:@"\\u2029"];
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
if([[NSThreadcurrentThread] isMainThread]) {
[self _evaluateJavascript:javascriptCommand];
}else{
dispatch_sync(dispatch_get_main_queue(), ^{
[self _evaluateJavascript:javascriptCommand];
});
}
}
JS拿到這個消息之后,進行消息的派遣陈瘦,首先要反序列化字符串得到幌甘,方法名和參數(shù),及方法的標識符痊项,需要一提的是锅风,這里先要檢查JS端緩存的的回調(diào)方法,通過標識符线婚,我們可以找到對應(yīng)的回調(diào)函數(shù)遏弱,如果有則調(diào)用方法盆均,沒有則直接調(diào)用方法塞弊,下面的代碼就是這樣的過程,至此OC調(diào)用JS完成
function_dispatchMessageFromObjC(messageJSON) {
setTimeout(function_timeoutDispatchMessageFromObjC() {
varmessage = JSON.parse(messageJSON);
varmessageHandler;
varresponseCallback;
if(message.responseId) {
responseCallback = responseCallbacks[message.responseId];
if(!responseCallback) {
return;
}
responseCallback(message.responseData);
deleteresponseCallbacks[message.responseId];
}else{
if(message.callbackId) {
varcallbackResponseId = message.callbackId;
responseCallback =function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData });
};
}
varhandler = messageHandlers[message.handlerName];
try{
handler(message.data, responseCallback);
}catch(exception) {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
}
if(!handler) {
console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
}
}
});
}
內(nèi)容涉及到的比較多 本人 水平有限理解的也不是很詳細,就先介紹這么多吧