• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/en/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**. The **.nm_register_func** property 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/napi_init.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> **NOTE**<br>You do not need to copy the above code as it is pre-configured in the **napi_init.cpp** file when the native C++ project is created.
50
51- Initialize the module.
52
53  Implement the mappings between the ArkTS and C++ APIs.
54
55  ```
56  // entry/src/main/cpp/napi_init.cpp
57  EXTERN_C_START
58  // Initialize the module.
59  static napi_value Init(napi_env env, napi_value exports) {
60      // Implement the mappings between the ArkTS and C++ APIs.
61      napi_property_descriptor desc[] = {
62          // You only need to copy the following two lines of code. Init has been configured in napi_init.cpp when the native C++ project is created.
63          {"callNative", nullptr, CallNative, nullptr, nullptr, nullptr, napi_default, nullptr},
64          {"nativeCallArkTS", nullptr, NativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr}
65      };
66      // Hook the CallNative and NativeCallArkTS APIs to the exports object.
67      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
68      return exports;
69  }
70  EXTERN_C_END
71
72  ```
73
74- Add JS APIs in the index.d.ts file.
75
76  ```
77  // entry/src/main/cpp/types/libentry/index.d.ts
78  export const callNative: (a: number, b: number) => number;
79  export const nativeCallArkTS: (cb: (a: number) => number) => number;
80  ```
81
82- Associate **index.d.ts** with **.cpp** in the **oh-package.json5** file.
83
84  ```
85  // entry/src/main/cpp/types/libentry/oh-package.json5
86  {
87    "name": "libentry.so",
88    "types": "./index.d.ts",
89    "version": "",
90    "description": "Please describe the basic information."
91  }
92  ```
93
94- Set CMake packaging parameters in the **CMakeLists.txt** file.
95
96  ```
97  # entry/src/main/cpp/CMakeLists.txt
98  cmake_minimum_required(VERSION 3.4.1)
99  project(MyApplication2)
100
101  set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
102
103  include_directories(${NATIVERENDER_ROOT_PATH}
104                      ${NATIVERENDER_ROOT_PATH}/include)
105
106  # Add a library named entry.
107  add_library(entry SHARED napi_init.cpp)
108  # Build the library to be linked to this executable.
109  target_link_libraries(entry PUBLIC libace_napi.z.so)
110  ```
111
112- Implement **CallNative** and **NativeCallArkTS**. The code is as follows:
113
114  ```
115  // entry/src/main/cpp/napi_init.cpp
116  static napi_value CallNative(napi_env env, napi_callback_info info)
117  {
118      size_t argc = 2;
119      // Declare the parameter array.
120      napi_value args[2] = {nullptr};
121
122      // Obtain input parameters and put them into the parameter array in sequence.
123      napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
124
125      // Obtain the parameters in sequence.
126      double value0;
127      napi_get_value_double(env, args[0], &value0);
128      double value1;
129      napi_get_value_double(env, args[1], &value1);
130
131      // Return the sum of the two numbers.
132      napi_value sum;
133      napi_create_double(env, value0 + value1, &sum);
134      return sum;
135  }
136
137  static napi_value NativeCallArkTS(napi_env env, napi_callback_info info)
138  {
139      size_t argc = 1;
140      // Declare the parameter array.
141      napi_value args[1] = {nullptr};
142
143      // Obtain input parameters and put them into the parameter array in sequence.
144      napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
145
146      // Create an int() as the input parameter of ArkTS.
147      napi_value argv = nullptr;
148      napi_create_int32(env, 2, &argv );
149
150      // Invoke the callback that is passed in, and return the result.
151      napi_value result = nullptr;
152      napi_call_function(env, nullptr, args[0], 1, &argv, &result);
153      return result;
154  }
155  ```
156
157
158## Calling C/C++ APIs on ArkTS
159
160On ArkTS, import the .so file that contains the processing logic on the native side to use C/C++ APIs.
161
162```
163// entry/src/main/ets/pages/Index.ets
164// Import the native APIs.
165import nativeModule from 'libentry.so'
166
167@Entry
168@Component
169struct Index {
170  @State message: string = 'Test Node-API callNative result: ';
171  @State message2: string = 'Test Node-API nativeCallArkTS result: ';
172  build() {
173    Row() {
174      Column() {
175        // Pressing the first button calls add(), which uses CallNative() in the native code to add the two numbers.
176        Text(this.message)
177          .fontSize(50)
178          .fontWeight(FontWeight.Bold)
179          .onClick(() => {
180            this.message += nativeModule.callNative(2, 3);
181            })
182        // Pressing the second button calls nativeCallArkTS, which corresponds to NativeCallArkTS in the native code. The ArkTS function is called on the native side.
183        Text(this.message2)
184          .fontSize(50)
185          .fontWeight(FontWeight.Bold)
186          .onClick(() => {
187            this.message2 += nativeModule.nativeCallArkTS((a: number)=> {
188                return a * 2;
189            });
190          })
191      }
192      .width('100%')
193    }
194    .height('100%')
195  }
196}
197```
198
199
200## Node-API Constraints
201
202
203### Naming Rules of .so Files
204
205The 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'**.
206
207
208### Registration
209
210- 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.
211
212- 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.
213
214
215### Multithread Processing
216
217Each 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:
218
219- The Node-API can be used only by JS threads.
220- The input parameter **env** of a native API can be bound to a JS thread only when the thread is created.
221- Data created using Node-APIs must be released before **env** is completely destroyed. Otherwise, memory leaks may be caused. In addition, accessing or using the data after **napi_env** is destroyed may cause the process to crash.
222
223Maintenance and testing measures have been added to address common errors. For details, see [Analysis of Exception Logs and Crashes Generated by the Use of Node-API](use-napi-about-crash.md).
224