Our company's APP is a typical hybrid development APP, which embeds front-end pages. To achieve the same effect as the native ones, the front-end pages cannot avoid calling some native methods. js calling method Let's take a look at how
Then if you want to call a native method, you can use the following function: function native (funcName, args = {}, callbackFunc, errorCallbackFunc) { // Check if the parameters are valid if (args && typeof args === 'object' && Object.prototype.toString.call(args).toLowerCase() === '[object object]' && !args.length) { args = JSON.stringify(args); } else { throw new Error('args does not conform to the specification'); } // Determine whether it is a mobile phone environment if (getIsMobile()) { //Call the callHandler method of the window.WebViewJavascriptBridge object window.WebViewJavascriptBridge.callHandler( funcName, args, (res) => { res = JSON.parse(res); if (res.code === 0) { return callbackFunc(res); } else { return errorCallbackFunc(res); } } ); } } Just pass in the method name, parameters and callback to be called. It first verifies the parameters and then calls the In addition, callbacks can be provided for native calls:
Next, let’s take a look at what Android The // define variable var messagingIframe; var sendMessageQueue = [];// Queue for sending messages var receiveMessageQueue = [];// Queue for receiving messages var messageHandlers = {};// Message handler var CUSTOM_PROTOCOL_SCHEME = 'yy';// Custom protocol var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/'; var responseCallbacks = {}; // Response callback var uniqueId = 1; I have simply translated it according to the variable name, and the specific use will be analyzed next. Next, the var WebViewJavascriptBridge = window.WebViewJavascriptBridge = { init: init, send: send, registerHandler: registerHandler, callHandler: callHandler, _fetchQueue: _fetchQueue, _handleMessageFromNative: _handleMessageFromNative }; You can see that it is an ordinary object with some methods mounted on it. I will not look at the specific methods for now, and continue below: var doc = document; _createQueueReadyIframe(doc); The function _createQueueReadyIframe (doc) { messagingIframe = doc.createElement('iframe'); messagingIframe.style.display = 'none'; doc.documentElement.appendChild(messagingIframe); } This method is very simple, just create a hidden // Create an event object of Events type (basic event module) var readyEvent = doc.createEvent('Events'); // Define the event name as WebViewJavascriptBridgeReady readyEvent.initEvent('WebViewJavascriptBridgeReady'); //Trigger the event through documentdoc.dispatchEvent(readyEvent); A custom event is defined here and dispatched directly. Other places can listen to this event just like listening to native events: document.addEventListener( 'WebViewJavascriptBridgeReady', function () { console.log(window.WebViewJavascriptBridge) }, false ); The purpose here, as I understand it, is that if the The self-executing function ends here. Next, let's take a look at the initial function init (messageHandler) { if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice'); } // No parameters are passed when init is called, so messageHandler=undefined WebViewJavascriptBridge._messageHandler = messageHandler; // Currently receiveMessageQueue is just an empty array var receivedMessages = receiveMessageQueue; receiveMessageQueue = null; for (var i = 0; i < receivedMessages.length; i++) { _dispatchMessageFromNative(receivedMessages[i]); } } From an initialization perspective, this function callHandler (handlerName, data, responseCallback) { _doSend({ handlerName: handlerName, data: data }, responseCallback); } After processing the parameters, the function _doSend (message, responseCallback) { // If a callback is provided if (responseCallback) { // Generate a unique callback id var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime(); // The callback is stored in the responseCallbacks object by id responseCallbacks[callbackId] = responseCallback; // Add the callback id to the message to be sent to native message.callbackId = callbackId; } //Add the message to the message queue sendMessageQueue.push(message); messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; } This method first generates a unique { handlerName, data, callbackId } Then add the function _fetchQueue () { // Convert the message queue we want to send into a string var messageQueueString = JSON.stringify(sendMessageQueue); // Clear the message queue sendMessageQueue = []; // Android cannot read the returned data directly, so it still communicates with Java through the iframe's src messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString); } After Android intercepts function _handleMessageFromNative (messageJSON) { // According to the logic of the previous init method, we know that receiveMessageQueue will be set to null, so we will go to the else branch if (receiveMessageQueue) { receiveMessageQueue.push(messageJSON); } else { _dispatchMessageFromNative(messageJSON); } } Take a look at what the function _dispatchMessageFromNative (messageJSON) { setTimeout(function () { // The original message sent back is of string type, converted to json var message = JSON.parse(messageJSON); var responseCallback; // The java call is completed, and the responseId sent back is the callbackId we sent to it before if (message.responseId) { // Get the callback method associated with the id from the responseCallbacks object responseCallback = responseCallbacks[message.responseId]; if (!responseCallback) { return; } // Execute callback, js calls Android method and successfully receives the message responseCallback(message.responseData); delete responseCallbacks[message.responseId]; } else { // ... } }); } function _dispatchMessageFromNative (messageJSON) { setTimeout(function () { if (message.responseId) { // ... } else { // Just like the message we send to the native can have an id, the message sent to us by the native can also have an id, and the native internal will also associate a callback with this id if (message.callbackId) { var callbackResponseId = message.callbackId; //If the frontend needs to reply to the native device, it should include the id sent by the native device before, so that the native device can find the corresponding callback through the id and execute it. responseCallback = function (responseData) { _doSend({ responseId: callbackResponseId, responseData: responseData }); }; } // We did not set a default _messageHandler, so it is undefined var handler = WebViewJavascriptBridge._messageHandler; // The message sent natively contains the name of the processing method if (message.handlerName) { // Use the method name to find out whether there is a corresponding processing method in the messageHandlers object handler = messageHandlers[message.handlerName]; } try { //Execute the processing method handler(message.data, responseCallback); } catch (exception) { if (typeof console !== 'undefined') { console.log('WebViewJavascriptBridge: WARNING: javascript handler threw.', message, exception); } } } }); } For example, if we want to listen to the native return key event, we first register it through the method of the window.WebViewJavascriptBridge.registerHandler('onBackPressed', () => { // Do something... }) function registerHandler (handlerName, handler) { messageHandlers[handlerName] = handler; } It's very simple. We store the event name and method we want to monitor in { handlerName: 'onBackPressed' } In this way, we can find the function we registered through At this point, the logic of mutual calls between 1.js calls native Generate a unique 2. Native call js First, the front end needs to register the events to be monitored in advance, save the event name and callback, and then the native will call the specified method of As you can see, the logic on both ios var CUSTOM_PROTOCOL_SCHEME_IOS = 'https'; var QUEUE_HAS_MESSAGE_IOS = '__wvjb_queue_message__'; Then when var BRIDGE_LOADED_IOS = '__bridge_loaded__'; function _createQueueReadyIframe (doc) { messagingIframe = doc.createElement('iframe'); messagingIframe.style.display = 'none'; if (isIphone()) { // This should be the bridge that iOS needs to load first messagingIframe.src = CUSTOM_PROTOCOL_SCHEME_IOS + '://' + BRIDGE_LOADED_IOS; } doc.documentElement.appendChild(messagingIframe); } Then, when function _fetchQueue () { var messageQueueString = JSON.stringify(sendMessageQueue); sendMessageQueue = []; return messageQueueString; // Return directly without going through iframe } Everything else is the same. Summarize This article analyzes the source code of This is the end of this article about learning the operating mechanism of jsBridge in one article. For more relevant content about the operating mechanism of jsBridge, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future! You may also be interested in:
|
<<: Detailed explanation of the actual process of master-slave synchronization of MySQL database
>>: Summary of Common Commands for Getting Started with MySQL Database Basics
Preface Because this is a distributed file system...
Table of contents Port-related concepts: Relation...
Table of contents 1. What is a cursor? 2. How to ...
Table of contents 1. Introduction 2. RC and RR is...
1. All tags must have a corresponding end tag Prev...
Use indexes to speed up queries 1. Introduction I...
A sophomore asked me how to install and configure...
Table of contents 1. Array flattening (also known...
1. Install mutt sudo apt-get install mutt 2. Inst...
Three ways to introduce CSS 1. Inline styles Adva...
The data type of MySQL is datetime. The data stor...
Detailed explanation of tinyMCE usage initializat...
Jellyka BeesAntique Handwriting [ank]* Jellyka Cut...
Preface As we all know, by default, the MySQL ins...
This article shares the specific code of Vue to a...