JSBridge 从源代码入手<二>

上一篇讲了加载html文件,html中的图片用OC进行下载并缓存传给html中进行显示的过程.
这一篇讲一下Native和JS之间如何进行相互调用method.
1: js调用Native并取得回调
在news.html中可以看到图片的点击方法

function onImageClick(picUrl){
    connectWebViewJavascriptBridge(function(bridge) {
        var allImage = document.querySelectorAll("img[esrc]");
        allImage = Array.prototype.slice.call(allImage, 0);
        var urls = new Array();
        var index = -1;
        var x = 0;
        var y = 0;
        var width = 0;
        var height = 0;
        allImage.forEach(function(image) {
            var imgUrl = image.getAttribute("esrc");
            var newLength = urls.push(imgUrl);
            if(imgUrl == picUrl || imgUrl == decodeURIComponent(picUrl)){
                index = newLength-1;
                x = image.getBoundingClientRect().left;
                y = image.getBoundingClientRect().top;
                x = x + document.documentElement.scrollLeft;
                y = y + document.documentElement.scrollTop;
                width = image.width;
                height = image.height;
                console.log("x:"+x +";y:" + y+";width:"+image.width +";height:"+image.height);
            }
        });
       console.log("检测到点击");
       //前面的都是拿取到一些参数进行包装
       //callHandler这个函数的实现是怎么样的的?
       bridge.callHandler('imageDidClicked', {'index':index,'x':x,'y':y,'width':width,'height':height}, function(response) {
            console.log("JS已经发出imgurl和index,同时收到回调,说明OC已经收到数据");

        });

    });
}

function callHandler(handlerName, data, responseCallback) {
        _doSend({ handlerName:handlerName, data:data }, responseCallback)
    }

    function _doSend(message, responseCallback) {
    //这里因为responseCallback是有值的所以
        if (responseCallback) {
        //这里是将时间变成ID用于保持唯一性
            var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime()
            responseCallbacks[callbackId] = responseCallback
            message['callbackId'] = callbackId
            //在这里将数据包装有  注: 记住这里传过去的包装数据
            //handlerName:handlerName
            //data:data
            //callbackId:callbackId
        }
        //这里数组保存message这个对象
        sendMessageQueue.push(message)
        //发送网络一遍OC中webview的代理拦截
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
    }

接下来看OC中如何进行拦截

//这个方法在上一篇已经讲过
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if (webView != _webView) { return YES; }
    NSURL *url = [request URL];
    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if ([_base isCorrectProcotocolScheme:url]) {
        if ([_base isCorrectHost:url]) {
        //这句代码将会执行WebViewJavascriptBridge._fetchQueue();js代码
        //这一段js代码返回的是什么呢?
        //function _fetchQueue() {
        // var messageQueueString = JSON.stringify(sendMessageQueue)
        //sendMessageQueue = []
        //return messageQueueString
    //}
    //可以看到拿到的是message包装的数据

            NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];

            [_base flushMessageQueue:messageQueueString];
        } else {
            [_base logUnkownMessage:url];
        }
        return NO;
    } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
        return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
    } else {
        return YES;
    }
}
//这一段代码要着重讲解下
 [_base flushMessageQueue:messageQueueString];
 //这就是_base执行的方法具体实现
 - (void)flushMessageQueue:(NSString *)messageQueueString{
    id messages = [self _deserializeMessageJSON:messageQueueString];
    //数据传送中的错误处理
    if (![messages isKindOfClass:[NSArray class]]) {
        NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [messages class], messages);
        return;
    }
    for (WVJBMessage* message in messages) {
        if (![message isKindOfClass:[WVJBMessage class]]) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
            continue;
        }
        [self _log:@"RCVD" json:message];
        //大家还记得我们刚才传过来的数据中都有什么呢? 不记得往上面翻翻看下其中并没有responseId
        NSString* responseId = message[@"responseId"];
        if (responseId) {
            WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
            responseCallback(message[@"responseData"]);
            [self.responseCallbacks removeObjectForKey:responseId];
        } else {
            WVJBResponseCallback responseCallback = NULL;
            NSString* callbackId = message[@"callbackId"];
            //是拥有callbackId 的
            if (callbackId) {
            //在这里定义了一个block等待被调用.
                responseCallback = ^(id responseData) {
                    if (responseData == nil) {
                        responseData = [NSNull null];
                    }

                    WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                    [self _queueMessage:msg];
                };
            } else {
                responseCallback = ^(id ignoreResponseData) {
                    // Do nothing
                };
            }

            WVJBHandler handler;
            //也是拥有handleName的
            if (message[@"handlerName"]) {
                handler = self.messageHandlers[message[@"handlerName"]];
            } else {
                handler = self.messageHandler;
            }
            //那么这里拿出来的handler是什么时候保存进去的呢?  到底是什么呢?  带着这个问题我们来看一下OC代码.
            if (!handler) {
                [NSException raise:@"WVJBNoHandlerException" format:@"No handler for message from JS: %@", message];
            }

            handler(message[@"data"], responseCallback);
        }
    }
}

看一下OC代码中的

//这段代码是干什么的呢?
//通过方法名我们知道这是在注册一个方法.
[_bridge registerHandler:@"imageDidClicked" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSInteger index = [[data objectForKey:@"index"] integerValue];
        NSInteger originX = [[data objectForKey:@"x"] floatValue];
        NSInteger originY = [[data objectForKey:@"y"] floatValue] + 64;
        NSInteger width = [[data objectForKey:@"width"] floatValue];
        NSInteger height = [[data objectForKey:@"height"] floatValue];
        UIImageView *tappedImageView = [[UIImageView alloc]init];
        [tappedImageView sd_setImageWithURL:[NSURL URLWithString:imgUrls[index]]];
        [tappedImageView setFrame:CGRectMake(originX, originY, width, height)];
        /**** 用一个白块遮住底层****/
        UIView *bgWhite = [[UIView alloc]initWithFrame:tappedImageView.frame];
        [bgWhite setBackgroundColor:[UIColor whiteColor]];
        [self.view addSubview:bgWhite];
        /**** ****/
        [self.view addSubview:tappedImageView];
        KYPhotoGallery *mainPhotoGallery = [[KYPhotoGallery alloc]initWithTappedImageView:tappedImageView andImageUrls:[[NSMutableArray alloc] initWithArray:@[imgUrls[index]]] andInitialIndex:1 dismissBlock:^{
            //完成后移除
            [tappedImageView removeFromSuperview];
            [bgWhite removeFromSuperview];
        }];
        mainPhotoGallery.imageViewArray = [[NSMutableArray alloc]initWithArray:@[tappedImageView]];
        [self presentViewController:mainPhotoGallery animated:NO completion:nil];
         }];
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
    _base.messageHandlers[handlerName] = [handler copy];
}
这是实现,  我们看到以handleName为key,handler为value,保存在字典中.

那我们就知道刚才取出来的handler是什么了.
那刚才注册的回调什么时候调用呢, 我们发现作者的这个方法是点击图片放大,我们发现并不需要JS那边做任何回应,所以只是JS通知OC这边需要大图浏览时,便会调用当前注册的这个回调.
那我们想看到如何回调的.我们就自己测试一下
我们在图片点击方法中JS代码最后加入

bridge.callHandler('testObjcCallback',{'data':'my name is js'},function(response){
                console.log(response);
            });

这一段代码.
跟前面一样,一直走到OC代码的注册代码被调用.

[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"testObjcCallback called: %@", data);
        responseCallback(@"Response from testObjcCallback");
    }];

我们看到这里的传参responseCallback 这个block被调用了.
那么这个block定义我们是知道的.我们看一下上面.刚才在flushMessageQueue方法中被定义的blcok会被调用.

responseCallback = ^(id responseData) {
                    if (responseData == nil) {
                        responseData = [NSNull null];
                    }

                    WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                    [self _queueMessage:msg];
                };

这个block中将OC处理完的数据包装起来调用_queueMessage:这个方法.
这个方法我们上一章讲过,会将数据传给JS端代码.JS端会根据ID值找到回调的匿名函数,会直接调用.
总结: 这就完成了JS调用原生并将处理完成之后的数据返回回去的操作.
同时Native调用JS的原理和JS调用Native的原理差不多, 也不是直接调用,而是通过JSBridge去调用.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值