React Native Communication Mechanism details, reactnative

Source: Internet
Author: User

React Native Communication Mechanism details, reactnative

Source: Bang blog (@ bang) Welcome to share Original Articles with bole headlines

React Native is the just-open-source framework of facebook. It can be used to develop Native apps directly using javascript. Let's not talk about whether the framework will be recognized by the public in the future. From the source code alone, there are many design ideas and implementation methods in the source code of this framework. In this article, we will first look at its most basic JavaScript-ObjectC communication mechanism (JS/OC ).

Overview

React Native uses JavaScriptCore of iOS as the parsing engine of JS, but does not use the features provided by JavaScriptCore that allow interaction between JS and OC. Instead, it implements a set of mechanisms by itself, this mechanism can be applied to all JS engines. Without JavaScriptCore, you can also use webview instead. In fact, webview is already used as the resolution engine in the project, it should be compatible with versions without JavascriptCore below iOS7.

Ordinary JS-OC communication is actually very simple, OC to JS to transfer information has a ready-made interface, like webview provided-stringByEvaluatingJavaScriptFromString method can be directly in the current context to execute a JS script, in addition, you can obtain the returned value after execution. The returned value is equivalent to passing information to OC by JS. Based on this, React Native defines a module method in OC through various means. JS can directly call this module method and seamlessly link the callback.

For example, OC defines a module RCTSQLManager, which has a method: query: successCallback:. JS can directly call RCTSQLManager. query and obtain the execution result through callback. :

123456789 //OC:@implement RCTSQLManager- (void)query:(NSString *)queryData successCallback:(RCTResponseSenderBlOCk)responseSender{     RCT_EXPORT();     NSString *ret = @"ret"     responseSender(ret);}@end
1234 //JS:RCTSQLManager.query("SELECT * FROM table", function(result) {     //result == "ret";});

Next, let's see how it is implemented.

Module configuration table

First, OC needs to tell JS what modules it has and what methods it has in the module. Only when JS knows that these methods are available can it call these methods. The implementation here is that OC generates a module configuration table and transmits it to JS. The configuration table contains information about methods in all modules and modules. Example:

1234567891011121314 {    "remoteModuleConfig": {        "RCTSQLManager": {            "methods": {                "query": {                    "type": "remote",                    "methodID": 0                }            },            "moduleID": 4        },        ...     },}

The OC end and JS end each have a bridge, both of which store the same module configuration table. When JS calls the OC module method, use the configuration table in bridge to convert the module method to the module ID and method ID to the OC. The OC finds the corresponding method for execution through the module configuration table in bridge. The above code is used as an example, the process is like this (do not consider callback first ):

Before learning about the calling process, let's take a look at the OC module configuration table. When we create an OC module, neither JS nor OC need to manually add some configurations for the new module. The module configuration table is automatically generated as long as there is a module in the project, this module will be added to the configuration table. How does this module configuration table be automatically generated? There are two steps:

1. Retrieve all module classes

Each module class implements the RCTBridgeModule interface. You can use the runtime interface objc_getClassList or objc_copyClassList to retrieve all classes in the project, and then determine whether the RCTBridgeModule interface is implemented one by one to find all module classes, implemented in the RCTBridgeModuleClassesByModuleID () method.

2. Obtain the method exposed to JS in the module

There can be many methods in a module, some of which can be exposed to JS for direct calls, some are private and do not want to be exposed to JS. How can we extract these exposure methods? The method I can think of is to develop rules for the method name to be exposed, such as using RCTExport _ as the prefix, and then using the runtime method class_getInstanceMethod to retrieve all method names, extract the method prefixed with RCTExport _. However, this is disgusting because each method must be prefixed. React Native uses another method like black magic to solve this problem: Compile attribute __.

In the above example, we can see the code in the module method: RCT_EXPORT (). Adding this macro to the method in the module can be exposed to JS without other rules, so what does this macro do? Let's take a look at its definition:

12 #define RCT_EXPORT(JS_name) __attribute__((used, section("__DATA,RCTExport" \))) static const char *__rct_export_entry__[] = { __func__, #JS_name }
 

This macro creates a section for the binary file by using the compilation attribute _, which belongs to the _ DATA segment named RCTExport and adds the current method name to the segment. During compilation, the compiler finds _ attribute _ for processing and adds corresponding content to the generated executable file. The effect can be seen from linkmap:

1234567891011121314 # Sections:# Address Size Segment Section0x100001670 0x000C0180 __TEXT __text...0x10011EFA0 0x00000330 __DATA RCTExport0x10011F2D0 0x00000010 __DATA __common0x10011F2E0 0x000003B8 __DATA __bss...  0x10011EFA0 0x00000010 [ 4] -[RCTStatusBarManager setStyle:animated:].__rct_export_entry__0x10011EFB0 0x00000010 [ 4] -[RCTStatusBarManager setHidden:withAnimation:].__rct_export_entry__0x10011EFC0 0x00000010 [ 5] -[RCTSourceCode getScriptText:failureCallback:].__rct_export_entry__0x10011EFD0 0x00000010 [ 7] -[RCTAlertManager alertWithArgs:callback:].__rct_export_entry__...
 

We can see that the data segment of the executable file has multiple RCTExport segments, and the content is the method to be exposed to JS. These contents can be obtained at run time in RCTBridge. the RCTExportedMethodsByModuleID () method of m obtains the content, extracts the class name and method name of each method, and exposes the content to the JS method in the extraction module.

The overall module class/method extraction is implemented in the RCTRemoteModulesConfig () method.

Call Process

Next, let's take a look at the detailed process of JS calling the OC module method, including callback. At this time, we need to refine the above call flowchart:

It looks a little complicated, but it should be easy to figure out the entire process step by step. In the figure, each process is labeled with a serial number. There are 11 steps in total from the initiation of a call to the execution of a callback. The following describes these steps in detail:

1. The JS end calls the method exposed by an OC module.

2. Break down the call in the previous step into ModuleName, MethodName, arguments, and then throw it to MessageQueue for processing.

During initialization, each module in the module configuration table generates the corresponding remoteModule object, and the object also generates a one-to-one method that corresponds to the module configuration table, in these methods, you can obtain the module name and method name, perform some processing on callback, and then hand it over to MessageQueue. Specifically, in the _ createBridgedModule of BatchedBridgeFactory. js, the entire implementation area contains 24 lines of code. Feel the magic of JS.

3. In this step, the JS callback function is cached in a member variable of MessageQueue and CallbackID is used to represent callback. In the module configuration table saved in MessageQueue, convert the ModuleName and MethodName passed in the previous step to ModuleID and MethodID.

4. Pass the ModuleID, MethodId, CallbackID, and other parameters obtained in the preceding steps to the OC. As for how to transfer the data, we will talk about it later.

5. OC receives messages and obtains the corresponding modules and methods through the module configuration table.

In fact, the module configuration table has been processed. Like JS, the OC also generates corresponding instances for each module in the module configuration table during initialization and caches them, each method in the Module also generates the corresponding RCTModuleMethod object. Here, the corresponding Module instance and RCTModuleMethod instance are obtained through ModuleID and MethodID. The specific implementation is in _ handleRequestNumber: moduleID: methodID: params :.

6. RCTModuleMethod processes each parameter passed by JS.

RCTModuleMethod can obtain each parameter type of the target method to be called by OC, and process the conversion from the JS type to the target type. All the numbers passed by JS are NSNumber, here it will be converted to the corresponding int, long, double, and Other types. More importantly, it will generate a block for the block type parameter.

For example, the-(void) select :( int) index response :( RCTResponseSenderBlock) callback method obtains the two parameter types: int and block. The two parameter types passed by JS are NSNumber, NSString (CallbackID) converts NSNumber to int, NSString (CallbackID) to a block, and the block content is to return the callback value and CallbackID to JS.

After these parameters are assembled, the corresponding OC module method is dynamically called through NSInvocation.

7. After the OC module method is called, the block callback is executed.

8. Call the block generated by RCTModuleMethod described in step 1.

9. Call the MessageQueue method invokeCallbackAndReturnFlushedQueue in JS with CallbackID and block parameters.

10. MessageQueue finds the JS callback method through CallbackID.

11. Call the callback method and pass the parameters brought by OC together to complete the callback.

The whole process is like this. In a simple summary, it is almost: JS function call to ModuleID/MethodID-> callback to CallbackID-> OC get Method Based on ID-> processing parameters-> call OC method-> callback CallbackID-> JS get callback through CallbackID to execute

Event Response

The above 4th step left a question: how does JS send data to OC and Let OC adjust the corresponding method?

The answer is through the return value. JS does not actively transmit data to OC. When the OC method is called, data such as ModuleID and MethodID will be added to a queue in step 3. When the OC calls any JS method, return the queue to OC, and then OC executes the method to be called in the queue.

I don't understand at the beginning. I can't directly call OC when designing Javascript. I need to trigger the call through the return value only when I call JS at OC. Can the whole program run well. Later I thought about the Event Response Mechanism in native development. When Will code be executed in native development? Only when an event is triggered, this event can be a startup event, a touch event, a timer event, a system event, or a callback event. In React Native, when these events occur, the OC will call the corresponding JS module method to handle them. After these events are processed, the JS will execute the method that the OC wants to execute, when no event occurs, no code will be executed, which is consistent with the Event Response Mechanism in native development.

Speaking of calling JS through OC, let's add that in fact, in addition to the remoteModules of the above OC modules, the module configuration table also saves the localModules of the js module and the methods of calling some JS modules by OC, it is also called by passing ModuleID and MethodID, and will go to the-enqueueJSCall: args: Method to pass two IDs and parameters to the BatchedBridge of JS. callFunctionReturnFlushedQueue, which is similar to how JS calls OC.

Summary

The entire React Native JS-OC communication mechanism is roughly like this, the key point is: modularization, module configuration table, transfer ID, encapsulation call, event response, its design idea and implementation method is worth learning for reference.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.