使用WebViewJavascriptBridge处理oc 和js交互问题

WebViewJavascriptBridge 是一个封装处理好的第三方库.功能非常强大.下面就简单介绍一下如何使用这个WebViewJavascriptBridge库,以供大家交流探讨和学习.

在学习前 先去github 下载这个库 ,下载: https://github.com/marcuswestin/WebViewJavascriptBridge

我们先看看这个库是如何处理的.

WebViewJavascriptBridge如其名字定义,就相当于一座桥梁,两端连接了Obj-C和JavaScript。它提供了OC和JS互调的方法接口,方法在互调之前,我们需要向对方注册我们的方法列表.

1. OC调用和回调JS

//1.1 JS注册OC的方法并实现回调OC

bridge.registerHandler('JS EchoDemo',function(data, responseCallback) {

console.log("-----JS demo called with:", data)

responseCallback(data)

})

//1.2 OC调用JS方法并实现回调函数

[self.bridge callHandler:@"JS EchoDemo"responseCallback:^(id responseData) {

NSLog(@"ObjC received response+++: %@", responseData);

}];

2. JS调用和回调OC


//2.1 OC注册JS的方法并实现回调JS

[self.bridge registerHandler:@"ObjC EchoDemo"handler:^(iddata, WVJBResponseCallback responseCallback) {

NSLog(@"ObjC Echo called with+++: %@", data);

responseCallback(data);

}];

//注意方法名称一致

//2.2 JS调用OC方法并实现回调函数

bridge.callHandler('ObjC EchoDemo', {'key':'value'},functionresponseCallback(responseData) {

console.log("------JS received response:", responseData)

})

先看下库的文件,一共有8个文件,而我们关心的文件只有3个,因为文件5,6是兼容WKWebView,实现原理都是一样的.

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

我们需要重点关注文件2,4,8中的代码,值得一提的是文件7,8是 注入JS 相关的文件

实现部分

1. 首先我们有一个bridge对象

@property(nonatomic, strong) WebViewJavascriptBridge *bridge;

2.初始化对象

self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];

bridge在初始化的时候会触发bridgeBase的初始化(第4个文件),实际上消息的处理和回调都是在bridgeBase中完成的,bridge的作用是:进行webView代理的传递,调用bridgeBase中消息的注册,调用和回调处理.

我们来看下bridgeBase的头文件中的属性,有消息队列数组,消息处理字典,响应回调字典和具体的消息处理对象

@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,与消息队列有关的URL定义与第一次加载的URL定义和处理方式是不同的,所以这里要进行区分,第一次加载的时候我们会注入(第8个文件)中的相关JS代码。消息队列相关的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代码,执行"_evaluateJavascript:"方法,实际调用的是webView中的"stringByEvaluatingJavaScriptFromString:"方法来注入JS代码,注入之后如果此时队列消息有消息的话就会进行消息的调用

- (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对象,这个对象里面有具体的有消息队列数组,消息处理字典,响应回调字典和具体的消息处理对象,和OC端的bridge是保持一致的,到这里完成了JS部分的初始化,整个初始化也就完成了

if(window.WebViewJavascriptBridge) {

return;

}

window.WebViewJavascriptBridge = {

registerHandler: registerHandler,

callHandler: callHandler,

_fetchQueue: _fetchQueue,

_handleMessageFromObjC: _handleMessageFromObjC

};

varmessagingIframe;

varsendMessageQueue = [];

varmessageHandlers = {};

6.前文提到了OC调用JS方法,首先需要在JS端进行方法的注册,我们来看方法的注册代码,函数定义部分是不变的,只要使用这个库,那么这一部分代码就是必须有的,调用函数传入回调部分的代码就是我们自己需要自定义的地方,这里的registerHandler中的函数名就是OC要调用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调用的时候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. 拿到对应的消息之后,首先要格式化成字符串对象,因为webView执行JavaScript脚本的时候接受的是一个字符串对象,之后进行一些列的转换处理,在下面的代码我们注意到有这样的字符串"WebViewJavascriptBridge._handleMessageFromObjC",这个就是我们之前注入的JS脚本里面对OC方法的处理的函数,于是我们成功的把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拿到这个消息之后,进行消息的派遣,首先要反序列化字符串得到,方法名和参数,及方法的标识符,需要一提的是,这里先要检查JS端缓存的的回调方法,通过标识符,我们可以找到对应的回调函数,如果有则调用方法,没有则直接调用方法,下面的代码就是这样的过程,至此OC调用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);

}

}

});

}

内容涉及到的比较多 本人 水平有限理解的也不是很详细,就先介绍这么多吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容