新葡京8455:Web端发消息给Native代码流程具体分析

作者:新闻中心

APP在营业进度在那之中,非常多时候供给做一些放大活动类的h5,那类h5会涉及跳转到原生的某部页面恐怕必要接收某些原生已经存在的效率。针对这种情状,很五人会说须求先前埋点一些一定的相互到webview上,但偶然布署赶不上安插,推广活动有广大中类型,哪个人也很难详尽列举供给埋点的互相,本着偷懒省事的准绳,自身早就设计了一套使用(WebViewJavascriptBridge

JSBrige类别直通车,由表及里领悟JS-Native的通信进度:
JSbridge连串剖判(一):JS-Native调用方法
JSbridge类别剖析(二):lzyzsd/JsBridge使用方法
JSbridge种类深入分析(三):lzyzsd/JsBridge源码解析
JSbridge种类分析(四):Web端发音讯给Native代码流程具体解析

本文使用了第三方框架:WebViewJavascriptBridge.h

  • Runtime)应用方案,收拾出来仅供后来者参考。

  • WebViewJavascriptBridge人机联作认知

  • Runtime幼功掌握

JSBridge使用进程中,运营景况关系java和js三个部分,调用流程在两片段间流转。剖判进程不独有要求精通android的开拓文化,还要求熟知JavaScript语法。

一、obj-c调用javascript的机制

  • UIWebView是iOS最常用的SDK之一,它有贰个stringByEvaluatingJavaScriptFromString方法能够将javascript嵌入页面中,通过这几个方式我们能够在iOS中与UIWebView中的网页成分交互作用。
    运用stringByEvaluatingJavaScriptFromString方法,须求等UIWebView中的页面加载成功之后去调用。
    采纳例子介绍:
    //获取当前页面的url
    [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"]
    //获取页面title:
    [webview stringByEvaluatingJavaScriptFromString:@"document.title"]
    //表单提交:
    [webView stringByEvaluatingJavaScriptFromString:@"document.forms[0].submit();"]

从上述方可以知道见stringByEvaluatingJavaScriptFromString不只好够实践js来获裁撤息,还是能够透过调用js对webView推行操作。 所以能够由此此函数来贯彻obj-c到js的交互作用。

备注:** stringByEvaluatingJavaScriptFromString这几个方法有个地点要求注意, 不能算bug, 但确实有毛病, 供给小心!**
如果stringByEvaluatingJavaScriptFromString执行的是带参数的js函数, 这个参数里面如果带有(r n ')等等, js那边收不到这个值, 这些带的需要转义,


新葡京8455 1sheji.png

以demo工程作为示范解说,暗中认可的Web端发音信的send方法。为了有支持驾驭,贴出了工艺流程调用中的关键代码,注意依据注释辨别代码出处(java文件或js文件)

二、 javascript调用obj-c

UIWebView对U福睿斯L的跳转举办阻挠,能够透过自定义左券来捕获央浼,在通过obj-c的stringByEvaluatingJavaScriptFromString来得到js对于obj-c函数的调用。

UIWebView对于跳转的拦截方法:

  • (BOOL)webView:(UIWebView)webView shouldStartLoadWithRequest:(NSURLRequest)request navigationType:(UIWebViewNavigationType)navigationType {

    }

实为上oc与js的通信交互作用正是出殡和下葬新闻,也即函数调用,只要在相互的进程科学的钦命好对方索要调用的函数和参数就ok

  • oc-->js stringBy伊娃luatingJavaScriptFromString,其参数是一NSString 字符串内容是js代码(那又足以是贰个js函数、一句js代码或他们的构成),当js函数有重返值或一句js代码有值重临可通过stringByEvaluatingJavaScriptFromString的再次回到值获取

  • js-->oc 利用webView的重定向原理(即重新在js中钦点document.location的值,此为一url),只要在这里个url字符串中按自定义的法则钦命好所需调用oc中的函数和参数,然后经过OC中的shouldStartLoadWithRequest函数去捕获管理央求,管理完最终,要是js还想赢得一些回到参数的话,相仿让oc去通过stringByEvaluatingJavaScriptFromString调用刚js传过来的回调js函数就能够,顺路把参数也一同传了。


  1. webview预先留下监听事件 clientDefineAction ,该事件可写入自定义的webview基类个中
  2. h5在急需互相的地点,使用WebViewJavascriptBridge传入特定data,格式如下:
  1. webview调用loadUrl加载页面,加载成功后注入WebViewJavascriptBridge.js

三、WebViewJavaScriptBridge实现机制

webViewJavaScriptBridge 包涵四个文本:

WebViewJavascriptBridge.h

WebViewJavascriptBridge.m

WebViewJavascriptBridge.js.txt

很白日衣绣:WebViewJavascriptBridge.js.txt首要用以衔接UIWebView中的web page,而WebViewJavascriptBridge.h/m则要害用于与ObjC的native code打交道。他们作为一个总体,其实起到了叁个“桥梁”的效果与利益,那多个公文封装了她们现实的并行管理方式,只开花出有个别对外的关系到业务管理的API,由此你在急需UIWebView与Native code人机联作的时候,引进该库,则没有必要考虑太多的交互作用上的标题。整个的Bridge对您来讲都是晶莹的,你倍感编制程序的时候,就如web编制程序的前端和后端同样清晰。他们使用交互作用如图所示:

新葡京8455 2

WebViewJavaScriptBridge交互图.png

上边大家对WebViewJavaScriptBridge的兑现做出深入分析。

3.1 WebViewJavaScriptBridge中的初叶化
WebViewJavaScriptBridge的初阶化过程如下:

1. 初始化需要使用的数据结果

2. 保存registerHandler的函数名与block

3. webView 加载完成载人js代码

新葡京8455 3

bridge初步化流程图.png

3.2 obj-c与js的交互
obj-c与js人机联作使用的机制是平等的,所以单项调用的拍卖是混在一道,所未来后遵从流程单独梳理,最终在以源码为例来注脚。
3.2.1 obj-c调用js过程:

- 1、obj-c保存回调的block,使用唯一码objc_cb_* 标识
- 2、obj-c拼装data(数据),handlerName(调用名),回调标识等信息
- 3、 对信息进行json拼装,及js一些字符的转码
- 4、 执行js端的分发消息的函数
- 5、js执行完成,执行函数块,调用register的responseCallback,发送responseId和responseData
- 6、 js拼装信息到消息队列中,URL重定向;obj-c端拦截请求,执行js代码拉取数据
- 7、obj-c端根据responseId来取得保存的block,执行block

大概流程图如下:

新葡京8455 4

obj-c调用js流程图.png

3.1.2 js调用obj-c的过程:

   - 1、js端保存data与handlerName的映射
   - 2、js端生成唯一码callbackId保存responseCallback映射,并封装callbackId到发送消息中
   - 3、js端封装后的消息放入发送消息队列中, 并执行URL重定向
   - 4、obj-c端拦截请求,判定是自定义协议后,执行js的_fetchQueue()拉取发送的信息
   - 5、obj-c端重新封装responcallback 的block,执行时调用_queueMessage进行消息分发
   - 6、从obj-c端的注册队列中获取handler的block,并执行
   - 7、执行后回调block,封装了responseId和responseData数据,并进行分发
   - 8、数据的特殊字符处理,调用js的函数_handleMessageFromObjC()来传递消息
   - 9、js获取分发的responseId,获取保存在队列中的function

大致的流程图如下:

新葡京8455 5

js调用obj-c流程图.png

3.2 源码解读
3.2.1 obj-c端源码
因js调用涉及到自定义左券的重定向,由此定义了商业事务及host

#define kCustomProtocolScheme @"wvjbscheme"
#define kQueueHasMessage@"__WVJB_QUEUE_MESSAGE__"

WebViewJavaScriptBridge的开端化因为涉嫌到OSX端WebView与iOS端UIWebView,故依靠分化的系统来做变量的合併定义,如下:

新葡京8455 6

OS X与iOS变量重定义.png

定义block,WVJBResponseCallback定义了回调block,js重返数据后回调。WVJBHandler定义了register的block,用作差异情形下的拍卖。**

typedef void(^WVJBResponseCallback)(id responseData);
typedef void(^WVJBHandler)(id data,WVJBResponseCallback responseCallback);

obj-c外界接口,用于native调用使用。当中register与callhandler函数封装数据后调用send管理。

新葡京8455 7

obj-c与js交互作用函数.png

send函数发送新闻到js端的源码:

新葡京8455 8

send封装到调用js代码.png

参数data为传送给js函数的参数,可为NSString、NSDictionary等,responseCallback则为obj-C的回调,此回调函数推行流程简述为“js注册函数实行完结后,会回到带有responseId的音讯,最终在obj-c会收取(回调存款和储蓄在responCallbacks辞典中卡塔尔,并施行”,handlerName则为js定义的函数名称。
源码中在_queueMessage方法实行逻辑决断:若在H5頁面大概js能源并未加载达成时,在obj-C的webview中就调用了js函数,则会把有关的操作(存款和储蓄为Message格式State of Qatar存款和储蓄在startupMessageQueue,等待相关财富加载完成(即在webview的webViewDidFinishLoad生命周期函数中试行存款和储蓄在startupMessageQueue的授命数组,实施完结并肃清改队列)再調用js的函数;否則若startupMessageQueue队列为空,则一向实施行强揭发在js端的webViewJavascriptBridge.handleMessageFromObjC函数,获取被调用的函数名和后野山参数,以致在objC的sendData:responseCallback:handlerName中设置的回调函数id—callbackId,最后推行js注册函数,并最终想obj-c端发送“doSend({ responseId:callbackResponseId, responseData:responseData }卡塔尔”格式的音信,待objC选择到消息,深入分析responseId,推行回调函数。

webView载入H5的delegate函数:

新葡京8455 9

finishLoad函数.png

H5 页面载入完毕后,载入js财富,在对起来音讯队列中的数据开展管理,并置nil。 在把delegate抛出。

新葡京8455 10

url拦截.png

有重定向供给时,首先判定scheme及host是自定的,使用_flushMessageQueue函数处理。

新葡京8455 11

obj-c端对js端处理.png

这段代码通过回传新闻的responseId来判别是回调依然call,来展开分歧的解封调用。

3.2.2 js端源码

js端定义的WebViewJavascriptBridge对象:

新葡京8455 12

js端WebViewJavascriptBridge类.png

js端的并行函数,逻辑管理,三个registerHandler和多个callHandler首要封装。_dosend函数实行信息封装放入发送音信队列,在爆发一个src(url scheme),供obj-c端shouldStartLoadWithRequest捕捉

新葡京8455 13

音信交互作用函数.png

obj-c端通过_fetchQueue(卡塔尔国获取发送的新闻

新葡京8455 14

js拉取发送的音信.png

js中管理来自objc的消息,推断同obj-c获取到js端的音信

新葡京8455 15

_dispatchMessageFromObjC.png

简轻易单代码

  • 导入WebViewJavascriptBridge (pods或直接下载个人赏识State of Qatar
  • #import <WebViewJavascriptBridge.h>

oc

//
//  ViewController.m
//  WebViewJSBridgeDemo
//
//  Created by taoyali on 16/8/1.
//  Copyright © 2016年 . All rights reserved.
//

#import "ViewController.h"
#import <WebViewJavascriptBridge.h>

@interface ViewController ()<UIWebViewDelegate>
@property (nonatomic, strong) WebViewJavascriptBridge *bridge;

@property (weak, nonatomic) IBOutlet UIWebView *webView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 2.加载网页
    NSString *indexPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
    NSString *appHtml = [NSString stringWithContentsOfFile:indexPath encoding:NSUTF8StringEncoding error:nil];
    NSURL *baseUrl = [NSURL fileURLWithPath:indexPath];
    [self.webView loadHTMLString:appHtml baseURL:baseUrl];

    // 3.开启日志
    [WebViewJavascriptBridge enableLogging];

    // 4.给webView建立JS和OC的沟通桥梁
    self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
    [self.bridge setWebViewDelegate:self];


    /* JS调用OC的API:访问相册 */
    [self.bridge registerHandler:@"openCamera" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"需要%@图片", data[@"count"]);

        UIImagePickerController *imageVC = [[UIImagePickerController alloc] init];
        imageVC.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        [self presentViewController:imageVC animated:YES completion:nil];
    }];


     /* JS调用OC的API:访问底部弹窗 */
    [self.bridge registerHandler:@"showSheet" handler:^(id data, WVJBResponseCallback responseCallback) {
        UIAlertController *vc = [UIAlertController alertControllerWithTitle:@"你猜我谈不谈?" message:@"不谈不谈,就不谈!!" preferredStyle:UIAlertControllerStyleActionSheet];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
        [vc addAction:cancelAction];
        [vc addAction:okAction];
        [self presentViewController:vc animated:YES completion:nil];
    }];


}

/*  获取用户信息  */
- (IBAction)getUserinfo {
    // 调用JS中的API
    [self.bridge callHandler:@"getUserInfo" data:@{@"userId":@"DX001"} responseCallback:^(id responseData) {
        NSString *userInfo = [NSString stringWithFormat:@"%@,姓名:%@,年龄:%@", responseData[@"userID"], responseData[@"userName"], responseData[@"age"]];
        UIAlertController *vc = [UIAlertController alertControllerWithTitle:@"从网页端获取的用户信息" message:userInfo preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
        [vc addAction:cancelAction];
        [vc addAction:okAction];
        [self presentViewController:vc animated:YES completion:nil];
    }];
}

/* 弹框显示消息 */
- (IBAction)showInfo {
    // 调用JS中的API
    [self.bridge callHandler:@"alertMessage" data:@"调用了js中的Alert弹窗!" responseCallback:^(id responseData) {

    }];
}

/* 控制界面动态跳转 */
- (IBAction)pushToNewWebSite {
    // 调用JS中的API
    [self.bridge callHandler:@"pushToNewWebSite" data:@{@"url":@"http://m.jd.com"} responseCallback:^(id responseData) {

    }];
}

/* 刷新界面 */
- (IBAction)reloadWebPage {
    [self.webView reload];
}

/* 插入新图片 */
- (IBAction)insertImgToWebPage {

    NSDictionary *dict = @{
                           @"url" : @"http://upload-images.jianshu.io/upload_images/1268909-0e394c67e1ce6666.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1240",
                           };
    // 调用JS中的API
    [self.bridge callHandler:@"insertImgToWebPage" data:dict responseCallback:^(id responseData) {

    }];

}

@end

html:

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
        <style>
            body{
                background-color:paleturquoise;
            }

            button{
                border:0;
                width: 150px;
                height: 35px;
                background-color: orangered;
                color: white;
                font-size: 16px;
                border-radius: 6px;
            }
        </style>
    </head>

    <body>
        <h2>JS调用OC中的方法</h2>
        <button id="btn">访问OC相册</button>
        <button id="btn1">调用OC提示窗</button>
        <p></p>
    </body>
    <script>
       // 这段代码是固定的,必须要放到js中
       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)
        }

        // 与OC交互的所有JS方法都要在这里注册,才能让OC和JS之间相互调用
       setupWebViewJavascriptBridge(function(bridge) {

           /* JS给OC提供公开的API, 在OC中可以手动调用此API, 并且可以接收OC中传过来的参数,同时可回调OC */

           // 获取用户信息
           bridge.registerHandler('getUserInfo', function(data, responseCallback) {
               console.log("OC中传递过来的参数:", data);
               // 把处理好的结果返回给OC
               responseCallback({"userID":"DX001", "userName":"旋之华", "age":"18", "otherName":"旋之华"})
           });

           //  弹框输出
           bridge.registerHandler('alertMessage', function(data, responseCallback) {
               alert(data);
           });

           //  动态跳转到京东商城
           bridge.registerHandler('pushToNewWebSite', function(data, responseCallback) {
               window.location.href = data.url;
           });

           bridge.registerHandler('insertImgToWebPage', function(data, responseCallback) {

                var img = document.createElement('img');
                img.src = data.url;
                img.width = 200;
                document.body.appendChild(img);

           });


           /* OC给JS提供公开的API, 在JS中可以手动调用此API, 并且可以接收OC中传过来的参数,同时可回调OC */

           // 调用OC中的打开相册方法
           document.getElementById('btn').onclick = function () {
               bridge.callHandler('openCamera', {'count':'10张'}, function responseCallback(responseData) {
                   console.log("OC中返回的参数:", responseData)
               });
           };

           document.getElementById('btn1').onclick = function () {
               bridge.callHandler('showSheet', '', function responseCallback(responseData) {
                   console.log("OC中返回的参数:", responseData)
               });
           };
       })
    </script>
</html>
bridge.callHandler('clientDefineAction', {'type':'1','controll':'XXViewController','params':'{'xx':'xx'}'}, function {});
//BridgeWebViewClient.java    
@Override
public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);

    if (BridgeWebView.toLoadJs != null) {
        //注入WebViewJavascriptBridge.js
        BridgeUtil.webViewLoadLocalJs(view, BridgeWebView.toLoadJs);
    }

    //处理消息队列中的消息
    if (webView.getStartupMessage() != null) {
        for (Message m : webView.getStartupMessage()) {
            webView.dispatchMessage(m);
        }
        webView.setStartupMessage(null);
    }
}

data格式表明:type:int 0或1 ,调用类型, 0:方法,1:页面``controll:string类型,需要调用的方法或者跳转的页面,需要ios跟android分别给到配置人员``params:string类型,客户端需要用的参数,需要ios跟android分别给到配置人员

  1. demo.html页面中式点心击"发音讯给Native"按键,触发WebViewJavascriptBridge.js中send方法的调用

对于controll须要传入的参数是实体类的意况,params以以下格式再次来到:

'params':{'xx':'xx','entity':{'UserEntity_userInfo':{'birthday':'1988-09-09','name':'hahaha'}}
//demo.html
function testClick() {
    var str1 = document.getElementById("text1").value;
    var str2 = document.getElementById("text2").value;

    //send message to native
    var data = {id: 1, content: "这是一个图片 <img src="a.png"/> testrnhahaha"};
    window.WebViewJavascriptBridge.send(
                data
                , function(responseData) {
                    document.getElementById("show").innerHTML = "repsonseData from java, data = "   responseData
                }
        );
}

params 中key为 entity 时标记深入解析的json为实体类,实体类为UserEntity,在controll中的参数名称叫userInfo。可看ViewController比较通晓:

  1. WebViewJavascriptBridge发送音讯调用_doSend,将音信存放在sendMessageQueue中,将responseCallbck放在responseCallbacks数组中,并设置message的callbackId。callbackId由uniqueId配适那个时候候间改动,用于后续查找responseCallback回调。改造iFrame的src,触发BridgeWebViewClient的shouldOverrideUrlLoading方法。

新葡京8455 16params_entity.png

  1. 解析data之后,调用Runtime解析器进行管理。
//WebViewJavascriptBridge.js
function send(data, responseCallback) {
        _doSend({
            data: data
        }, responseCallback);
}

//sendMessage add message, 触发native处理 sendMessage
//此时,生成callbackId用于html页面中send方法的回调
function _doSend(message, responseCallback) {
    if (responseCallback) {
        var callbackId = 'cb_'   (uniqueId  )   '_'   new Date().getTime();
        responseCallbacks[callbackId] = responseCallback;
        message.callbackId = callbackId;
    }

    sendMessageQueue.push(message);
    //更换src,前缀为yy://__QUEUE_MESSAGE__/
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME   '://'   QUEUE_HAS_MESSAGE;
}

ps: 涉及公司事情,已去掉一部分代码

  1. shouldOverrideUrlLoading方法依照url的前缀,踏向了BridgeWebView的flushMessageQueue方法。
#pragma mark ------ 监听预留事件- clientDefineAction:(NSDictionary *)dict{ NSInteger type = [StringHelper formatterIntValue:[dict objectForKey:@"type"]]; NSString *controllName = [dict objectForKey:@"controll"]; NSDictionary *params = [dict objectForKey:@"params"]; if (type == 0) { //调方法 SEL methodsel = NSSelectorFromString([NSString stringWithFormat:@"%@:",controllName]); if ([self.methodHandle respondsToSelector:methodsel]) { //methodHandle为常用方法集合,需要自己整理 } }else{ //调页面 NSString *class = [controllName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; if ([class isEqualToString:@"tab1"]) { //跳转tab1 return; } if ([class isEqualToString:@"tab2"]) { //跳转tab2 return; } if ([class isEqualToString:@"tab3"]) { //跳转tab3 return; } if ([class isEqualToString:@"tab4"]) { //跳转tab4 return; } [self findRightRoutedWithClass:class Params:params]; }}

runtime解析器

//BridgeWebViewClient.java
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    try {
        url = URLDecoder.decode(url, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) { // 如果是返回数据
         //url以yy://return/开头
        webView.handlerReturnData(url);
        return true;
    } else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) { //
         //url以yy://开头
        webView.flushMessageQueue();
        return true;
    } else {
        return super.shouldOverrideUrlLoading(view, url);
    }
}
- findRightRoutedWithClass:(NSString *)class Params:(NSDictionary *)params{ const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding]; // 根据字符串返回类 Class rClass = objc_getClass(className); if { return; } // 创建对象 id instance = [[rClass alloc] init]; WS; if  { // 对该对象赋值属性 NSDictionary * propertys = params; [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if ([key isEqualToString:@"entity"]) { //对象类型属性 NSDictionary *entitys = [propertys objectForKey:key]; [entitys enumerateKeysAndObjectsUsingBlock:^(NSString *ckey, id entityObj, BOOL *s) { NSArray *entityParamArray = [ckey componentsSeparatedByString:@"_"]; if (entityParamArray.count > 0) { const char *className = [entityParamArray[0] cStringUsingEncoding:NSASCIIStringEncoding]; Class entity = objc_getClass(className); entity = [entity objectWithKeyValues:entityObj];//这里使用MJExtension解析对象 if ([weakSelf checkIsExistPropertyWithInstance:instance verifyPropertyName:entityParamArray[1]]) { // 利用kvc赋值 [instance setValue:entity forKey:entityParamArray[1]]; } } }]; }else{ //普通类型属性 if ([weakSelf checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) { // 利用kvc赋值 [instance setValue:obj forKey:key]; } } }]; } [self.currentParentVc pushNormalViewController:instance];}- checkIsExistPropertyWithInstance:instance verifyPropertyName:(NSString *)verifyPropertyName{ unsigned int outCount, i; // 获取对象里的属性列表 objc_property_t * properties = class_copyPropertyList([instance class], &outCount); for (i = 0; i < outCount; i  ) { objc_property_t property =properties[i]; // 属性名转成字符串 NSString *propertyName = [[NSString alloc] initWithCString:property_getName encoding:NSUTF8StringEncoding]; // 判断该属性是否存在 if ([propertyName isEqualToString:verifyPropertyName]) { free(properties); return YES; } } free(properties); //这里处理UIViewController特定属性 if ([verifyPropertyName isEqualToString:@"title"]) { return YES; } return NO;}
  1. flushMessageQueue通过loadUrl调用到WebViewJavascriptBridge.js中的_fetchQueue(State of Qatar方法,并注册了三个回调函数。注意,BridgeWebView的loadUrl方法不但奉行了js语句调用,还将相应的回调函数放在responseCallbacks中, key是_fetchQueue。回调函数的具体内容后续解析

h5 关键js:

<script>$(function(){ new FastClick(document.body); setupWebViewJavascriptBridge(function { //跳转 $(".shareBtn").click(function(){// bridge.callHandler('clientDefineAction', {'type':'1','controll':'XxxViewController','params':{'xxx':'xxx'}}, function {});// bridge.callHandler('clientDefineAction', {'type':'1','controll':'tab1'}, function {}); bridge.callHandler('clientDefineAction', {'type':'0','controll':'shareToQQ','params':{'shareUrl': 'http://www.baidu.com','shareTitle':'奇异果子','shareDesc':'抢购价:¥0.10'}}, function {}); }); });}); function setupWebViewJavascriptBridge { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push; } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement; WVJBIframe.style.display = 'none'; WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0);}</script>
//BridgeWebView.java
void flushMessageQueue() {
    if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
        loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
            @Override
            public void onCallBack(String data) {
                // deserializeMessage
                ......
            }
        });
    }
}

public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
    this.loadUrl(jsUrl);
    responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback);
}

demo这里就不给出去了,等整合治理下一篇再给,>_<//

  1. _fetchQueue方法将sendMessageQueue数组中的全数音信,系列化为json字符串,通过改造iFrame的src,触发shouldOverrideUrlLoading方法
//WebViewJavascriptBridge.js
// 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容
function _fetchQueue() {
    //将消息
    var messageQueueString = JSON.stringify(sendMessageQueue);
    sendMessageQueue = [];
    //android can't read directly the return data, so we can reload iframe src to communicate with java
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME   '://return/_fetchQueue/'   encodeURIComponent(messageQueueString);
}
  1. 具体参谋步骤4中代码,shouldOverrideUrlLoading方法依据url的前缀,走入了BridgeWebView的handlerReturnData方法。根据传入的url([yy://return/_fetchQueue/[]( <img src="a.png"/> testrnhahaha"},"callbackId":"cb_2_1501580166257"}]),获取回调函数的functionName为_fetchQueue。根据步骤5,最后调用了flushMessageQueue中loadUrl传入的回调函数。
//BridgeWebView.java
void handlerReturnData(String url) {
    String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
    CallBackFunction f = responseCallbacks.get(functionName);
    String data = BridgeUtil.getDataFromReturnUrl(url);

    if (f != null) {
        f.onCallBack(data);
        responseCallbacks.remove(functionName);
        return;
    }
}
  1. 分析_fetchQueue的回调函数,将json数据转载为Message数组,依次拍卖Message数组中的新闻。这次示例中唯有demo.html中调用的send方法,数据为{"data":{"id":1,"content":"那是叁个图片 <img src="a.png"/> testrnhahaha"},"callbackId":"cb_2_1501580166257"}。那时音讯的responseId为空,callbackId是在步骤3的_doSend中生,用于标志send方法的回调。为了促成该意义,以下代码中布局了responseMsg来达成Java到Js的调用。由于demo.html的send方法不是assigned handler,所以采纳defaultHanlder来管理音信。
//BridgeWebView.java
void flushMessageQueue() {
    if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
        loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
            @Override
            public void onCallBack(String data) {
                // deserializeMessage
                List<Message> list = null;
                try {
                    list = Message.toArrayList(data);
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
                if (list == null || list.size() == 0) {
                    return;
                }

                 //依次处理message
                for (int i = 0; i < list.size(); i  ) {
                    Message m = list.get(i);
                    String responseId = m.getResponseId();
                    // 是否是response。即Java发消息给web时,传入的回调函数
                    if (!TextUtils.isEmpty(responseId)) {
                        CallBackFunction function = responseCallbacks.get(responseId);
                        String responseData = m.getResponseData();
                        function.onCallBack(responseData);
                        responseCallbacks.remove(responseId);
                    } else {
                        CallBackFunction responseFunction = null;
                        // if had callbackId
                        final String callbackId = m.getCallbackId();
                        //即Web端发送消息给Native时,注册的回调函数。需要通过Native->JS触发
                        if (!TextUtils.isEmpty(callbackId)) {
                            responseFunction = new CallBackFunction() {
                                @Override
                                public void onCallBack(String data) {
                                    //构造回调消息
                                    Message responseMsg = new Message();
                                    responseMsg.setResponseId(callbackId);
                                    responseMsg.setResponseData(data);
                                    queueMessage(responseMsg);
                                }
                            };
                        } else {
                            responseFunction = new CallBackFunction() {
                                @Override
                                public void onCallBack(String data) {
                                    // do nothing
                                }
                            };
                        }
                        BridgeHandler handler;
                        if (!TextUtils.isEmpty(m.getHandlerName())) {
                            //assigned handler
                            handler = messageHandlers.get(m.getHandlerName());
                        } else { //默认handler
                            handler = defaultHandler;
                        }
                        if (handler != null){
                            handler.handler(m.getData(), responseFunction);
                        }
                    }
                }
            }
        });
    }
}
  1. BridgeWebView中的defaultHandler变量由setDefaultHandler函数设置,依据MainActivity的早先化语句webView.setDefaultHandler(new DefaultHandler(卡塔尔国卡塔尔(قطر‎,最后调用DefaultHandler的handle方法。此中CallBackFunction即上个步骤中设置的responseFunction,该方法组织了responseMsg,设置了responseId(即send方法的callbackId)和data。
//DefaultHandler.java
@Override
public void handler(String data, CallBackFunction function) {
    if(function != null){
        function.onCallBack("DefaultHandler response data");
    }
}

新葡京8455,9、 responseMsg通过BridgeWebView的queueMessage,经过一多种调用,最后踏入WebViewJavascriptBridge.js的_handleMessageFromNative

//提供给native使用,
function _dispatchMessageFromNative(messageJSON) {
        setTimeout(function() {
        var message = JSON.parse(messageJSON);
        var responseCallback;
        //java call finished, now need to call js callback function
        if (message.responseId) {回调消息的处理
            responseCallback = responseCallbacks[message.responseId];
            if (!responseCallback) { //未注册回调
                    return;
            }
            responseCallback(message.responseData); //回调,即进入demo.html的
            delete responseCallbacks[message.responseId];
        } else {
            //直接发送。Native调用Web的消息
            if (message.callbackId) {
                var callbackResponseId = message.callbackId;
                responseCallback = function(responseData) {
                    _doSend({
                         responseId: callbackResponseId,
                         responseData: responseData
                     });
                 };
            }

            var handler = WebViewJavascriptBridge._messageHandler;
                if (message.handlerName) {
                    handler = messageHandlers[message.handlerName];
                }
                //查找指定handler
                try {
                    handler(message.data, responseCallback);
                } catch (exception) {
                    if (typeof console != 'undefined') {
                        console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
                    }
                }
            }
        });
    }

Native提供assign handler供Web端调用

  1. 同上手续1。webview加载页面,达成WebViewJavascriptBridge.js的注入。
  2. webview调用rigisterHandler,注册可供js调用的handler。最后handler在java端寄放在webview的messageHandlers变量中
//MainActivity.java
webView.registerHandler("submitFromWeb", new BridgeHandler() {

    @Override
    public void handler(String data, CallBackFunction function) {
        Log.i(TAG, "handler = submitFromWeb, data from web = "   data);
                function.onCallBack("submitFromWeb exe, response data 中文 from Java");
    }

});

//BridgeWebView.java
public void registerHandler(String handlerName, BridgeHandler handler) {
    if (handler != null) {
        messageHandlers.put(handlerName, handler);
    }
}
  1. demo.html中调用Native端提供的办法,名称叫submitFromWeb. WebViewJavascriptBridge提供callHandler作为调用的归并接口,参数分别为handlerName,handlerparams,回调函数。
//demo.html
function testClick1() {
    var str1 = document.getElementById("text1").value;
    var str2 = document.getElementById("text2").value;

    //call native method
    window.WebViewJavascriptBridge.callHandler(
        'submitFromWeb'
         , {'param': '中文测试'}
         , function(responseData) {
                    document.getElementById("show").innerHTML = "send get responseData from java, data = "   responseData
        }
    );
}
  1. callHandler最后调用_doSend方法。
//WebViewJavascriptBridge.js
function callHandler(handlerName, data, responseCallback) {
    _doSend({
        handlerName: handlerName,
        data: data
    }, responseCallback);
}
  1. 同上手续4、5、6、7。
  2. 处理_fetchQueue的回调函数时,那个时候的json数据为{"handlerName":"submitFromWeb","data":{"param":"中文测验"},"callbackId":"cb_3_150158905一九二零"。该数额反种类化的Message对象getHandlerName为subminFromWeb。依照messageHandlers找到步骤第22中学MainActivity注册的不二法门,达成JS到Java的调用。
  3. 拍卖JS调用完成的回调函数,同上步骤9。

Web端提供assign handler或default handler供Native端调用

与上述手续相近,各位可具体剖析

本文由新葡京8455发布,转载请注明来源

关键词: