在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
我司的APP是一个典型的混合开发APP,内嵌的都是前端页面,前端页面要做到和原生的效果相似,就避免不了调用一些原生的方法, js调用方式先来看一下,
然后如果要调用某个原生方法可以使用下面的函数: function native (funcName, args = {}, callbackFunc, errorCallbackFunc) { // 校验参数是否合法 if (args && typeof args === 'object' && Object.prototype.toString.call(args).toLowerCase() === '[object object]' && !args.length) { args = JSON.stringify(args); } else { throw new Error('args不符合规范'); } // 判断是否是手机环境 if (getIsMobile()) { // 调用window.WebViewJavascriptBridge对象的callHandler方法 window.WebViewJavascriptBridge.callHandler( funcName, args, (res) => { res = JSON.parse(res); if (res.code === 0) { return callbackFunc(res); } else { return errorCallbackFunc(res); } } ); } } 传入要调用的方法名、参数和回调即可,它先校验了一下参数,然后会调用 此外也可以提供回调供原生调用:
接下来看一下 安卓
// 定义变量 var messagingIframe; var sendMessageQueue = [];// 发送消息的队列 var receiveMessageQueue = [];// 接收消息的队列 var messageHandlers = {};// 消息处理器 var CUSTOM_PROTOCOL_SCHEME = 'yy';// 自定义协议 var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/'; var responseCallbacks = {};// 响应的回调 var uniqueId = 1; 根据变量名简单翻译了一下,具体用处接下来会分析。接下来定义了 var WebViewJavascriptBridge = window.WebViewJavascriptBridge = { init: init, send: send, registerHandler: registerHandler, callHandler: callHandler, _fetchQueue: _fetchQueue, _handleMessageFromNative: _handleMessageFromNative }; 可以看到就是一个普通的对象,上面挂载了一些方法,具体方法暂时不看,继续往下: var doc = document; _createQueueReadyIframe(doc); 调用了 function _createQueueReadyIframe (doc) { messagingIframe = doc.createElement('iframe'); messagingIframe.style.display = 'none'; doc.documentElement.appendChild(messagingIframe); } 这个方法很简单,就是创建了一个隐藏的 // 创建一个Events类型(基础事件模块)的事件(Event)对象 var readyEvent = doc.createEvent('Events'); // 定义事件名为WebViewJavascriptBridgeReady readyEvent.initEvent('WebViewJavascriptBridgeReady'); // 通过document来触发该事件 doc.dispatchEvent(readyEvent); 这里定义了一个自定义事件,并直接派发了,其他地方可以像通过监听原生事件一样监听该事件: document.addEventListener( 'WebViewJavascriptBridgeReady', function () { console.log(window.WebViewJavascriptBridge) }, false ); 这里的用处我理解就是当该 到这里自执行函数就结束了,接下来看一下最开始的 function init (messageHandler) { if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice'); } // init调用的时候没有传参,所以messageHandler=undefined WebViewJavascriptBridge._messageHandler = messageHandler; // 当前receiveMessageQueue也只是一个空数组 var receivedMessages = receiveMessageQueue; receiveMessageQueue = null; for (var i = 0; i < receivedMessages.length; i++) { _dispatchMessageFromNative(receivedMessages[i]); } } 从初始化的角度来看,这个 function callHandler (handlerName, data, responseCallback) { _doSend({ handlerName: handlerName, data: data }, responseCallback); } 处理了一下参数又调用了 function _doSend (message, responseCallback) { // 如果提供了回调的话 if (responseCallback) { // 生成一个唯一的回调id var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime(); // 回调通过id存储到responseCallbacks对象上 responseCallbacks[callbackId] = responseCallback; // 把该回调id添加到要发送给native的消息里 message.callbackId = callbackId; } // 消息添加到消息队列里 sendMessageQueue.push(message); messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; } 这个方法首先把调用原生方法时的回调函数通过生成一个唯一的 { handlerName, data, callbackId } 接着把该 function _fetchQueue () { // 把我们要发送的消息队列转成字符串 var messageQueueString = JSON.stringify(sendMessageQueue); // 清空消息队列 sendMessageQueue = []; // 安卓无法直接读取返回的数据,因此还是通过iframe的src和java通信 messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString); } 安卓拦截到 function _handleMessageFromNative (messageJSON) { // 根据之前的init方法的逻辑我们知道receiveMessageQueue是会被设置为null的,所以会走else分支 if (receiveMessageQueue) { receiveMessageQueue.push(messageJSON); } else { _dispatchMessageFromNative(messageJSON); } } 看一下 function _dispatchMessageFromNative (messageJSON) { setTimeout(function () { // 原生发回的消息是字符串类型的,转成json var message = JSON.parse(messageJSON); var responseCallback; // java调用完成,发回的responseId就是我们之前发送给它的callbackId if (message.responseId) { // 从responseCallbacks对象里取出该id关联的回调方法 responseCallback = responseCallbacks[message.responseId]; if (!responseCallback) { return; } // 执行回调,js调用安卓方法后到这里顺利收到消息 responseCallback(message.responseData); delete responseCallbacks[message.responseId]; } else { // ... } }); }
function _dispatchMessageFromNative (messageJSON) { setTimeout(function () { if (message.responseId) { // ... } else { // 和我们传给原生的消息可以带id一样,原生传给我们的消息也可以带一个id,同时原生内部也会通过这个id关联一个回调 if (message.callbackId) { var callbackResponseId = message.callbackId; //如果前端需要再给原生回消息的话那么就带上原生之前传来的id,这样原生就可以通过id找到对应的回调并执行 responseCallback = function (responseData) { _doSend({ responseId: callbackResponseId, responseData: responseData }); }; } // 我们并没有设置默认的_messageHandler,所以是undefined var handler = WebViewJavascriptBridge._messageHandler; // 原生发送的消息里面有处理方法名称 if (message.handlerName) { // 通过方法名称去messageHandlers对象里查找是否有对应的处理方法 handler = messageHandlers[message.handlerName]; } try { // 执行处理方法 handler(message.data, responseCallback); } catch (exception) { if (typeof console !== 'undefined') { console.log('WebViewJavascriptBridge: WARNING: javascript handler threw.', message, exception); } } } }); } 比如我们要监听原生的返回键事件,我们先通过 window.WebViewJavascriptBridge.registerHandler('onBackPressed', () => { // 做点什么... })
function registerHandler (handlerName, handler) { messageHandlers[handlerName] = handler; } 很简单,把我们要监听的事件名和方法都存储到 { handlerName: 'onBackPressed' } 这样就可以通过 到此,安卓环境的 1.js调用原生生成一个唯一的 2.原生调用js首先前端需要事先注册要监听的事件,把事件名和回调保存起来,然后原生在某个时刻会调用 可以看到, ios
var CUSTOM_PROTOCOL_SCHEME_IOS = 'https'; var QUEUE_HAS_MESSAGE_IOS = '__wvjb_queue_message__'; 然后 var BRIDGE_LOADED_IOS = '__bridge_loaded__'; function _createQueueReadyIframe (doc) { messagingIframe = doc.createElement('iframe'); messagingIframe.style.display = 'none'; if (isIphone()) { // 这里应该是ios需要先加载一下bridge messagingIframe.src = CUSTOM_PROTOCOL_SCHEME_IOS + '://' + BRIDGE_LOADED_IOS; } doc.documentElement.appendChild(messagingIframe); } 再然后是 function _fetchQueue () { var messageQueueString = JSON.stringify(sendMessageQueue); sendMessageQueue = []; return messageQueueString;// 直接返回,不需要通过iframe } 其他部分都是一样的。 总结本文分析了一下 到此这篇关于一篇文章学会jsBridge的运行机制的文章就介绍到这了,更多相关jsBridge 运行机制内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论