WebViewJavascriptBridge源码探究--看OC和JS交互过程(介绍了WebViewJavascriptBridge的实现过程)

来源:互联网 发布:温州一洋淘宝培训中心 编辑:程序博客网 时间:2024/05/16 12:36

开始了解WebViewJavascriptBridge时候,一直想:Server端应该引用了一个JS,为什么在哪都找不到呢,官方Example里也没有。

后来想了想,又看了这个文章才知道,原来WebViewJavascriptBridge是在APP端生成的。也就是说,APP调用网页里,给网页里嵌入了一段JS。

http://www.cnblogs.com/tandaxia/p/5699886.html

今天把实现OC代码和JS代码交互的第三方库WebViewJavascriptBridge源码看了下,oc调用js方法我们是知道的,系统提供了stringByEvaluatingJavaScriptFromString函数

。现在主要是了解js是如何调用oc方法的,分享下探究过程。

   源码不多,就一个头文件WebViewJavascriptBridge.h和实现文件WebViewJavascriptBridge.m, 和一个js文件,实现在js那边可以调用oc方法,也可以在oc里面调用js方法。

先上图,实现简单的oc和js互相调用的demo, 另外附加一个模拟项目中用到的oc和js互相调用场景:

  

 

一、然后说说js调用oc方法的原理,它们是如何实现的?库文件三个

 

我们跟踪下oc控制器加载UIWebView的过程和js调用oc方法过程

 

1、程序启动,在自定义控制器里,创建一个WebViewJavascriptBridge对象时,会加载WebViewJavascriptBridge.js.txt文件,里面是初始js代码

     在这个js里面,创建了一个WebViewJavascriptBridge脚本对象,另外创建一个隐藏的iframe标签:每次js调用oc方法,都是修改iframe标签的src来触发UIWebView的代理监听方法

复制代码
;(function() {    if (window.WebViewJavascriptBridge) { return }    var messagingIframe    var sendMessageQueue = []    var receiveMessageQueue = []    var messageHandlers = {}        var CUSTOM_PROTOCOL_SCHEME = 'wvjbscheme'    var QUEUE_HAS_MESSAGE = '__WVJB_QUEUE_MESSAGE__'        var responseCallbacks = {}    var uniqueId = 1        function _createQueueReadyIframe(doc) {        messagingIframe = doc.createElement('iframe')        messagingIframe.style.display = 'none'        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE        doc.documentElement.appendChild(messagingIframe)    }    function init(messageHandler) {        if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice') }        WebViewJavascriptBridge._messageHandler = messageHandler        var receivedMessages = receiveMessageQueue        receiveMessageQueue = null        for (var i=0; i<receivedMessages.length; i++) {            _dispatchMessageFromObjC(receivedMessages[i])        }    }    function send(data, responseCallback) {        _doSend({ data:data }, responseCallback)    }        function registerHandler(handlerName, handler) {        messageHandlers[handlerName] = handler    }        function callHandler(handlerName, data, responseCallback) {        _doSend({ handlerName:handlerName, data:data }, responseCallback)    }        function _doSend(message, responseCallback) {        if (responseCallback) {            var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime()            responseCallbacks[callbackId] = responseCallback            message['callbackId'] = callbackId        }        sendMessageQueue.push(message); //将字典放入数组        //修改iframe标签的src属性,UIWebView监听执行代理方法        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;    }    function _fetchQueue() {  //json数组转成json字符串        var messageQueueString = JSON.stringify(sendMessageQueue)        sendMessageQueue = []        return messageQueueString    }    function _dispatchMessageFromObjC(messageJSON) {        setTimeout(function _timeoutDispatchMessageFromObjC() {            var message = JSON.parse(messageJSON)            var messageHandler                        if (message.responseId) {                var responseCallback = responseCallbacks[message.responseId]                if (!responseCallback) { return; }                responseCallback(message.responseData)                delete responseCallbacks[message.responseId]            } else {                var responseCallback                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]                }                                try {                    handler(message.data, responseCallback)                } catch(exception) {                    if (typeof console != 'undefined') {                        console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception)                    }                }            }        })    }        function _handleMessageFromObjC(messageJSON) {        if (receiveMessageQueue) {            receiveMessageQueue.push(messageJSON)        } else {            _dispatchMessageFromObjC(messageJSON)        }    }    window.WebViewJavascriptBridge = {        init: init,        send: send,        registerHandler: registerHandler,        callHandler: callHandler,        _fetchQueue: _fetchQueue,        _handleMessageFromObjC: _handleMessageFromObjC    }    var doc = document    _createQueueReadyIframe(doc)    var readyEvent = doc.createEvent('Events')    readyEvent.initEvent('WebViewJavascriptBridgeReady')    readyEvent.bridge = WebViewJavascriptBridge    doc.dispatchEvent(readyEvent)})();
复制代码

 

2、UIWebView加载我们自定义的html页面TestJSBridge.html, 里面有脚本注册js调用oc方法标识,和oc调用js标识

复制代码
<html>    <head>        <meta charset="utf-8"/>        <style type="text/css">            html { font-family:Helvetica; color:#222; background:#D5FFFD; border: 5px dashed blue;}            .rowH3{margin: 0px; text-align: center;}            .jsBtn{font-size: 18px;}        </style>    </head>    <body>        <h3 class="rowH3">测试OC和JS互相调用</h3>        <button class="jsBtn" id="jsBtn">JS调用OC方法</button>        <div id="logDiv"><!-- 打印日志 --></div>    </body></html><script type="text/javascript">    window.onerror = function(err) {        printLog(err);    }        function connectWebViewJavascriptBridge(callback) {        if (window.WebViewJavascriptBridge) {            callback(WebViewJavascriptBridge);        } else {            document.addEventListener('WebViewJavascriptBridgeReady', function() {                callback(WebViewJavascriptBridge);            }, false);        }    }        var uniqueId = 1;    //日志打印方法    function printLog(data) {        var logObj = document.getElementById('logDiv');        var el = document.createElement('div');        el.className = 'logLine';        el.innerHTML = uniqueId++ + ': ' + JSON.stringify(data); //json转字符串        if (logObj.children.length) { logObj.insertBefore(el, logObj.children[0]) }        else { logObj.appendChild(el) }    }        //初始化调用函数connectWebViewJavascriptBridge    connectWebViewJavascriptBridge(function(bridge) {                bridge.init(function(message, responseCallback) {});                                             //注册js响应方法,响应OC调用,标识objc_Call_JS_Func        bridge.registerHandler('objc_Call_JS_Func', function(data, responseCallback) {            printLog(data);  //打印oc传过来的参数        });        //给标签按钮设置点击事件        var callbackButton = document.getElementById('jsBtn');        callbackButton.onclick = function(e) {            e.preventDefault();            //注册标识js_Call_Objc_Func,便于js给IOS发送消息            bridge.callHandler('js_Call_Objc_Func', {id: 1, info: 'hello, iOS, 我从js那边过来!'},  function(response) { });        }    });    </script>
复制代码

 

3、点击html标签按钮,触发js事件

1
2
3
4
5
6
7
//给标签按钮设置点击事件
        varcallbackButton = document.getElementById('jsBtn');
        callbackButton.onclick =function(e) {
            e.preventDefault();
            //注册标识js_Call_Objc_Func,便于js给IOS发送消息
            bridge.callHandler('js_Call_Objc_Func', {id: 1, info:'hello, iOS, 我从js那边过来!'}, function(response) { });
        }

 我们跟踪bridge.callHandler方法,进入WebViewJavascriptBridge.js

  var CUSTOM_PROTOCOL_SCHEME = 'wvjbscheme'

  var QUEUE_HAS_MESSAGE = '__WVJB_QUEUE_MESSAGE__'

    messagingIframe是个iframe标签,点击我们自定义html按钮标签,触发js事件,最后进入callHandler -->  _doSend ,

当messagingIframe标签src重新赋值时,会触发UIWebView的代理方法(src的值一直是:wvjbscheme://__WVJB_QUEUE_MESSAGE__ ,也可自定义,这个在进入oc UIWebView代理方法时会用来作为判断标识)。

跟踪后面执行的过程:

  

至此,js调用oc成功

总结js调用oc过程:

-->   触发js事件

-->   把要传入参数和自定义注册标识“js_Call_Objc_Func”存入js数组sendMessageQueue

 -->  重新赋值iframe标签的src属性,触发UIWebView代理方法, 根据src的值进入相应处理方法中

-->   在oc方法里面调用js方法_fetchQueue, 获取js数组里面所有的参数  

-->   根据传入的自定义注册标识 js_Call_Objc_Func  从oc字典_messageHandlers找出匹配block, 最后执行block,里面有我们自定义处理的后续代码

 

二、oc调用js过程

从oc内部发起

-- > 调用bridge的callHandler方法,传入需要的参数和自定义注册标识

--> 最后使用UIWebView系统方法stringByEvaluatingJavaScriptFromString调用js脚本WebViewJavascriptBridge._handleMessageFromObjC 完成参数的传递

  

---------------------------------------------  end --------------------------------------

DEMO下载

github地址:https://github.com/xiaotanit/Tan_WebViewJavaScriptBridge

 

另外记录一个UIWebView不能加载带中文参数的url问题:

假设加载url为:http://baidu.com/?search=博客园

这样UIWebView加载这个带中文参数的url, 是不能显示的,需要把中文进行转义,才能显示。

使用字符串方法stringByAddingPercentEncodingWithAllowedCharacters对中文进行转义

复制代码
NSString *str = @"http://baidu.com/?search=博客园";  //  str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];  str = [str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "].invertedSet];     NSURL *url = [NSURL URLWithString:str];

  NSURLRequest *request = [NSURLRequestrequestWithURL:url];

复制代码

 

原文链接:http://www.cnblogs.com/tandaxia/p/5699886.html



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 一直有电话骚扰怎么办 老打电话骚扰我怎么办 总有电话骚扰怎么办 狗一直抽搐不停怎么办 右眼皮一直抽搐怎么办 打包机不停的倒带不切带怎么办 频繁被电话骚扰怎么办 一直被骚扰电话怎么办 有电话短信骚扰怎么办 收到大量验证码怎么办? 每天有陌生电话怎么办 电话不停被骚扰怎么办 水龙头的乱溅怎么办 水龙头坏了喷水怎么办 水龙头水流很散怎么办 自来水往外喷水怎么办 手机有骚扰电话怎么办 半夜到南宁机场怎么办 得了水痘坐火车怎么办 香港账户被限制怎么办 司法拍卖车扣分怎么办 速递易已超期怎么办 e栈快递换微信怎么办 e栈快递柜打不开怎么办 小孩耳朵塞东西怎么办 把隐形眼镜吃了怎么办 街道分车位不公平怎么办 服务区修车被宰怎么办 2018高速逃费怎么办 高速不停车卡怎么办 独立显卡显存不够怎么办 rx470d买不到了怎么办? 南方避险基金到期怎么办 高铁票认证失败怎么办 高铁票三个小孩怎么办 12306取消三次后怎么办 外国人护照过期了怎么办 行李丢飞机上怎么办 学生票没次数了怎么办 去美国行李超重怎么办 12306密码找回失败怎么办