1# Node-API Development Process 2 3 4To implement cross-language interaction using Node-API, you need to register and load modules based on the Node-API mechanism first. 5 6 7- ArkTS/JS: Import the .so library and call C++ APIs. 8 9- Native: Implement module registration via a .cpp file. You need to declare the name of the library to register and define the mappings between the native and JS/ArkTS APIs in the callbacks registered. 10 11 12The following demonstrates how to implement cross-language interaction by implementing **add()** in ArkTS/JS code and **Add()** in native code. 13 14 15## Creating a Native C++ Project 16 17- In DevEco Studio, choose **New** > **Create Project**, select the **Native C++** template, click **Next**, select the API version, set the project name, and click **Finish**. 18 19- The main code of the project created consists of two parts: **cpp** and **ets**. For details about the project structure, see <!--RP1-->[C++ Project Directory Structure](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-project-structure-V5)<!--RP1End-->. 20 21 22## Implementing Native APIs 23 24- Set module registration information. 25 26 When a native module is imported in ArkTS, the .so file will be loaded. During the loading process, the **napi_module_register** method is called to register the module with the system and call the module initialization function. 27 28 napi_module has two key properties: **.nm_register_func** and **.nm_modname**. **.nm_register_func** defines the module initialization function, and **.nm_modname** defines the module name, that is, the name of the .so file imported by ArkTS. 29 30 ``` 31 // entry/src/main/cpp/hello.cpp 32 33 // Information about the module. Record information such as the Init() function and module name. 34 static napi_module demoModule = { 35 .nm_version = 1, 36 .nm_flags = 0, 37 .nm_filename = nullptr, 38 .nm_register_func = Init, 39 .nm_modname = "entry", 40 .nm_priv = nullptr, 41 .reserved = {0}, 42 }; 43 44 // When the .so file is loaded, this function is automatically called to register the demoModule module with the system. 45 extern "C" __attribute__((constructor)) void RegisterDemoModule() { 46 napi_module_register(&demoModule); 47 } 48 ``` 49 50- Initialize the module. 51 52 Implement the mappings between the ArkTS and C++ APIs. 53 54 ``` 55 // entry/src/main/cpp/hello.cpp 56 EXTERN_C_START 57 // Initialize the module. 58 static napi_value Init(napi_env env, napi_value exports) { 59 // Implement the mappings between the ArkTS and C++ APIs. 60 napi_property_descriptor desc[] = { 61 {"callNative", nullptr, CallNative, nullptr, nullptr, nullptr, napi_default, nullptr}, 62 {"nativeCallArkTS", nullptr, NativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr}, 63 }; 64 // Hook the CallNative and NativeCallArkTS APIs to the exports object. 65 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 66 return exports; 67 } 68 EXTERN_C_END 69 70 // Basic module information. 71 static napi_module demoModule = { 72 .nm_version = 1, 73 .nm_flags = 0, 74 .nm_filename = nullptr, 75 .nm_register_func = Init, 76 .nm_modname = "entry", 77 .nm_priv = nullptr, 78 .reserved = {0}, 79 }; 80 ``` 81 82- Add JS APIs in the index.d.ts file. 83 84 ``` 85 // entry/src/main/cpp/types/libentry/index.d.ts 86 export const callNative: (a: number, b: number) => number; 87 export const nativeCallArkTS: (cb: (a: number) => number) => number; 88 ``` 89 90- Associate **index.d.ts** with **.cpp** in the **oh-package.json5** file. 91 92 ``` 93 { 94 "name": "libentry.so", 95 "types": "./index.d.ts", 96 "version": "", 97 "description": "Please describe the basic information." 98 } 99 ``` 100 101- Set CMake packaging parameters in the **CMakeLists.txt** file. 102 103 ``` 104 # entry/src/main/cpp/CMakeLists.txt 105 cmake_minimum_required(VERSION 3.4.1) 106 project(MyApplication2) 107 108 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 109 110 include_directories(${NATIVERENDER_ROOT_PATH} 111 ${NATIVERENDER_ROOT_PATH}/include) 112 113 # Add a library named entry. 114 add_library(entry SHARED hello.cpp) 115 # Build the library to be linked to this executable. 116 target_link_libraries(entry PUBLIC libace_napi.z.so) 117 ``` 118 119- Implement **CallNative** and **NativeCallArkTS**. The code is as follows: 120 121 ``` 122 // entry/src/main/cpp/hello.cpp 123 static napi_value CallNative(napi_env env, napi_callback_info info) 124 { 125 size_t argc = 2; 126 // Declare the parameter array. 127 napi_value args[2] = {nullptr}; 128 129 // Obtain input parameters and put them into the parameter array in sequence. 130 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 131 132 // Obtain the parameters in sequence. 133 double value0; 134 napi_get_value_double(env, args[0], &value0); 135 double value1; 136 napi_get_value_double(env, args[1], &value1); 137 138 // Return the sum of the two numbers. 139 napi_value sum; 140 napi_create_double(env, value0 + value1, &sum); 141 return sum; 142 } 143 144 static napi_value NativeCallArkTS(napi_env env, napi_callback_info info) 145 { 146 size_t argc = 1; 147 // Declare the parameter array. 148 napi_value args[1] = {nullptr}; 149 150 // Obtain input parameters and put them into the parameter array in sequence. 151 napi_get_cb_info(env, info, &argc, args , nullptr, nullptr); 152 153 // Create an int() as the input parameter of ArkTS. 154 napi_value argv = nullptr; 155 napi_create_int32(env, 2, &argv ); 156 157 // Invoke the callback that is passed in, and return the result. 158 napi_value result = nullptr; 159 napi_call_function(env, nullptr, args[0], 1, &argv, &result); 160 return result; 161 } 162 ``` 163 164 165## Calling C/C++ APIs on ArkTS 166 167On ArkTS, import the .so file that contains the processing logic on the native side to use C/C++ APIs. 168 169``` 170// entry/src/main/ets/pages/Index.ets 171// Import the native APIs. 172import nativeModule from 'libentry.so' 173 174@Entry 175@Component 176struct Index { 177 @State message: string = 'Test Node-API callNative result: '; 178 @State message2: string = 'Test Node-API nativeCallArkTS result: '; 179 build() { 180 Row() { 181 Column() { 182 // Pressing the first button calls add(), which uses CallNative() in the native code to add the two numbers. 183 Text(this.message) 184 .fontSize(50) 185 .fontWeight(FontWeight.Bold) 186 .onClick(() => { 187 this.message += nativeModule.callNative(2, 3); 188 }) 189 // Pressing the second button calls nativeCallArkTS, which corresponds to NativeCallArkTS in the native code. The ArkTS function is called on the native side. 190 Text(this.message2) 191 .fontSize(50) 192 .fontWeight(FontWeight.Bold) 193 .onClick(() => { 194 this.message2 += nativeModule.nativeCallArkTS((a: number)=> { 195 return a * 2; 196 }); 197 }) 198 } 199 .width('100%') 200 } 201 .height('100%') 202 } 203} 204``` 205 206 207## Node-API Constraints 208 209 210### Naming Rules of .so Files 211 212The case of the module name to import must be the same as that registered. For example, if the module name is **entry**, the .so file name must be **libentry.so**, and the **nm_modname** field in **napi_module** must be **entry**. When importing the module in ArkTS, use **import xxx from 'libentry.so'**. 213 214 215### Registration 216 217- To prevent conflicts with symbols in the .so file, add **static** to the function corresponding to **nm_register_func**. For example, the **Init()** function in this document. 218 219- The name of the module registration entry, that is, the function modified by **__attribute__((constructor))** must be unique. For example, the **RegisterDemoModule** function in this document. 220 221 222### Multithread Processing 223 224Each engine instance corresponds to a JS thread. The objects of an instance cannot be operated across threads. Otherwise, the application crashes. Observe the following rules: 225 226- The Node-API can be used only by JS threads. 227 228- The input parameter **env** of a native API can be bound to a JS thread only when the thread is created. 229