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: Call C++ APIs by importing the related .so library. 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 callback 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 17In 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 19The main code of the project created consists of two parts: **cpp** and **ets**. 20 21**Project Directory Structure** 22 23 24 25- **entry > src > main > cpp > types**: directory for C++ API description files. 26 27- **entry > src > main > cpp > types > libentry > index.d.ts**: file containing C++ APIs, including the API names, input parameters, and return values. 28 29- **entry > src > main > cpp > types > libentry > oh-package.json5**: file for configuring the entry and name of the third-party .so package. 30 31- **entry > src > main > cpp > CMakeLists.txt**: C++ source code configuration file, which provides the CMake build script. 32 33- **entry > src > main > cpp > hello.cpp**: file containing the C++ APIs of your application. 34 35- **entry > src > main > ets**: directory for ArkTS source code. 36 37For details about more projects, see [C++ Project Directory Structure](https://developer.harmonyos.com/en/docs/documentation/doc-guides-V3/project_overview-0000001053822398-V3#section3732132312179). 38 39 40## Implementing Native APIs 41 42- Set module registration information. 43 44 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. 45 46 napi_module has two key attributes: **.nm_register_func** and **.nm_modname**. The former defines the module initialization function, and the latter specifies the module name, that is, the name of the .so file imported by ArkTS. 47 48 ``` 49 // entry/src/main/cpp/hello.cpp 50 51 // Information about the module. Record information such as the Init() function and module name. 52 static napi_module demoModule = { 53 .nm_version = 1, 54 .nm_flags = 0, 55 .nm_filename = nullptr, 56 .nm_register_func = Init, 57 .nm_modname = "entry", 58 .nm_priv = nullptr, 59 .reserved = {0}, 60 }; 61 62 // When the .so file is loaded, this function is automatically called to register the demoModule module with the system. 63 extern "C" __attribute__((constructor)) void RegisterDemoModule() { 64 napi_module_register(&demoModule); 65 } 66 ``` 67 68- Initialize the module. 69 70 Implement the mappings between the ArkTS and C++ APIs. 71 72 ``` 73 // entry/src/main/cpp/hello.cpp 74 EXTERN_C_START 75 // Initialize the module. 76 static napi_value Init(napi_env env, napi_value exports) { 77 // Implement the mappings between the ArkTS and C++ APIs. 78 napi_property_descriptor desc[] = { 79 {"callNative", nullptr, CallNative, nullptr, nullptr, nullptr, napi_default, nullptr}, 80 {"nativeCallArkTS", nullptr, NativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr}, 81 }; 82 // Embed the CallNative and NativeCallArkTS methods to the exports object. 83 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 84 return exports; 85 } 86 EXTERN_C_END 87 88 // Basic module information. 89 static napi_module demoModule = { 90 .nm_version = 1, 91 .nm_flags = 0, 92 .nm_filename = nullptr, 93 .nm_register_func = Init, 94 .nm_modname = "entry", 95 .nm_priv = nullptr, 96 .reserved = {0}, 97 }; 98 ``` 99 100- Add JS APIs in the index.d.ts file. 101 102 ``` 103 // entry/src/main/cpp/types/libentry/index.d.ts 104 export const callNative: (a: number, b: number) => number; 105 export const nativeCallArkTS: (cb: (a: number) => number) => number; 106 ``` 107 108- Associate **index.d.ts** with **.cpp** in the **oh-package.json5** file. 109 110 ``` 111 { 112 "name": "libentry.so", 113 "types": "./index.d.ts", 114 "version": "", 115 "description": "Please describe the basic information." 116 } 117 ``` 118 119- Set CMake packaging parameters in the **CMakeLists.txt** file. 120 121 ``` 122 # entry/src/main/cpp/CMakeLists.txt 123 cmake_minimum_required(VERSION 3.4.1) 124 project(MyApplication2) 125 126 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 127 128 include_directories(${NATIVERENDER_ROOT_PATH} 129 ${NATIVERENDER_ROOT_PATH}/include) 130 131 # Add a library named entry. 132 add_library(entry SHARED hello.cpp) 133 # Build the library to be linked to this executable. 134 target_link_libraries(entry PUBLIC libace_napi.z.so) 135 ``` 136 137- Implement **CallNative** and **NativeCallArkTS**. The code is as follows: 138 139 ``` 140 // entry/src/main/cpp/hello.cpp 141 static napi_value CallNative(napi_env env, napi_callback_info info) 142 { 143 size_t argc = 2; 144 // Declare the parameter array. 145 napi_value args[2] = {nullptr}; 146 147 // Obtain input parameters and put them into the parameter array in sequence. 148 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 149 150 // Obtain the parameters in sequence. 151 double value0; 152 napi_get_value_double(env, args[0], &value0); 153 double value1; 154 napi_get_value_double(env, args[1], &value1); 155 156 // Return the sum of the two numbers. 157 napi_value sum; 158 napi_create_double(env, value0 + value1, &sum); 159 return sum; 160 } 161 162 static napi_value NativeCallArkTS(napi_env env, napi_callback_info info) 163 { 164 size_t argc = 1; 165 // Declare the parameter array. 166 napi_value args[1] = {nullptr}; 167 168 // Obtain input parameters and put them into the parameter array in sequence. 169 napi_get_cb_info(env, info, &argc, args , nullptr, nullptr); 170 171 // Create an int() as the input parameter of ArkTS. 172 napi_value argv = nullptr; 173 napi_create_int32(env, 2, &argv ); 174 175 // Invoke the callback that is passed in, and return the result. 176 napi_value result = nullptr; 177 napi_call_function(env, nullptr, args[0], 1, &argv, &result); 178 return result; 179 } 180 ``` 181 182 183## Calling C/C++ APIs on ArkTS 184 185On ArkTS, import the .so file that contains the Native processing logic. This allows C/C++ methods to be called on ArkTS. 186 187``` 188// entry/src/main/ets/pages/Index.ets 189// Import the native APIs. 190import nativeModule from 'libentry.so' 191 192@Entry 193@Component 194struct Index { 195 @State message: string = 'Test Node-API callNative result: '; 196 @State message2: string = 'Test Node-API nativeCallArkTS result: '; 197 build() { 198 Row() { 199 Column() { 200 // Pressing the first button calls add(), which uses CallNative() in the native code to add the two numbers. 201 Text(this.message) 202 .fontSize(50) 203 .fontWeight(FontWeight.Bold) 204 .onClick(() => { 205 this.message += nativeModule.callNative(2, 3); 206 }) 207 // Pressing the second button calls nativeCallArkTS, which corresponds to NativeCallArkTS in the native code. The ArkTS function is called on the native side. 208 Text(this.message2) 209 .fontSize(50) 210 .fontWeight(FontWeight.Bold) 211 .onClick(() => { 212 this.message2 += nativeModule.nativeCallArkTS((a: number)=> { 213 return a * 2; 214 }); 215 }) 216 } 217 .width('100%') 218 } 219 .height('100%') 220 } 221} 222``` 223 224 225## Node-API Constraints 226 227 228### Naming Rules of .so Files 229 230The 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'**. 231 232 233### Registration 234 235- 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. 236 237- 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. 238 239 240### Multithread Processing 241 242Each engine instance corresponds to a JS thread. The objects of an instance cannot be operated across threads. Otherwise, the application may crash. Observe the following rules: 243 244- The Node-API can be used only by JS threads. 245 246- The input parameter **env** of a native API can be bound to a JS thread only when the thread is created. 247