使用WebViewJavascriptBridge處理oc 和js交互問題

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)容涉及到的比較多 本人 水平有限理解的也不是很詳細,就先介紹這么多吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泪姨,一起剝皮案震驚了整個濱河市游沿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肮砾,老刑警劉巖诀黍,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仗处,居然都是意外死亡眯勾,警方通過查閱死者的電腦和手機枣宫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吃环,“玉大人也颤,你說我怎么就攤上這事∮羟幔” “怎么了翅娶?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長好唯。 經(jīng)常有香客問我竭沫,道長,這世上最難降的妖魔是什么骑篙? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任蜕提,我火速辦了婚禮,結(jié)果婚禮上替蛉,老公的妹妹穿的比我還像新娘贯溅。我一直安慰自己,他們只是感情好躲查,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布它浅。 她就那樣靜靜地躺著,像睡著了一般镣煮。 火紅的嫁衣襯著肌膚如雪姐霍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天典唇,我揣著相機與錄音镊折,去河邊找鬼。 笑死介衔,一個胖子當著我的面吹牛恨胚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炎咖,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼赃泡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了乘盼?” 一聲冷哼從身側(cè)響起升熊,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绸栅,沒想到半個月后级野,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡粹胯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年蓖柔,在試婚紗的時候發(fā)現(xiàn)自己被綠了辰企。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡况鸣,死狀恐怖蟆豫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情懒闷,我是刑警寧澤十减,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站愤估,受9級特大地震影響帮辟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜玩焰,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一由驹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昔园,春花似錦蔓榄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荤西,卻和暖如春澜搅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背邪锌。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工勉躺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人觅丰。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓饵溅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妇萄。 傳聞我的和親對象是個殘疾皇子蜕企,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容