Using N-APIs in Application Projects
In OpenHarmony, you can use the N-APIs in C APIs to implement interaction between ArkTS/TS/JS and C/C++. The N-API names are the same as those in the third-party Node.js. Currently, OpenHarmony supports some N-APIs. For details about the APIs supported, see arkui_napi.
How to Develop
The DevEco Studio provides a default project that uses N-APIs. You can choose File > New > Create Project to create a Native C++ project. After the project is created, the cpp directory is generated in the entry/src/main directory. You can use the N-APIs to develop C/C++ code (native code).
You can import the native .so file for ArkTS/TS/JS programming. For example, you can import hello from 'libhello.so' to use the libhello.so capability and pass the ArkTS/TS/JS object named hello to the ArkTS/TS/JS APIs of the application. You can use this object to invoke the N-APIs in cpp.
Basic Features
The N-APIs implement interaction between ArkTS/TS/JS and C/C++. The following provides two HelloWorld project examples:
- Define an N-API method Add(), which is called by ArkTS with two numbers passed in. The N-API Add() method adds the two numbers and returns the result to ArkTS.
- Define an N-API method named NativeCallArkTS, which is called by ArkTS with an ArkTS function passed in. The NativeCallArkTS method invokes this ArkTS function and returns the result to ArkTS.
The following describes:
- How an ArkTS method invokes a C++ method.
- How a C++ method invokes an ArkTS method.
The project has the following files:
- entry\src\main\cpp\hello.cpp: contains the N-API logic.
- entry\src\main\ets\pages\index.ets: contains the ArkTS logic.
- entry\src\main\cpp\types\libentry\index.d.ts: contains the declaration of the N-APIs exposed to ArkTS.
The following provides the comments for the files. Other parts in the project are the same as those in the native default project.
// entry\src\main\cpp\hello.cpp
// Include the N-API header file.
#include "napi/native_api.h"
// N-API method, which has only two input parameters. You do not need to modify them.
// napi_env is the current running context.
// napi_callback_info contains related information, including parameters passed from ArkTS.
static napi_value Add(napi_env env, napi_callback_info info)
{
// Number of parameters to be obtained from ArkTS. napi_value can be regarded as the representation of the ArkTS value in the N-API method.
size_t argc = 2;
napi_value args[2] = {nullptr};
// From info(), obtain the parameters passed from ArkTS. In this example, two ArkTS parameters, arg[0] and arg[1], are obtained.
napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
// Convert the obtained ArkTS parameters to the type that can be processed by N-API. In this example, the two numbers passed from ArkTS are converted to the double type.
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);
// N-API service logic, which is adding two numbers in this example.
double nativeSum = value0 + value1;
// Convert the N-API service logic processing result to an ArkTS value and return the value to ArkTS.
napi_value sum;
napi_create_double(env, nativeSum , &sum);
return sum;
}
static napi_value NativeCallArkTS(napi_env env, napi_callback_info info)
{
// Number of parameters to be obtained from ArkTS. napi_value can be regarded as the representation of the ArkTS value in the N-API method.
size_t argc = 1;
napi_value args[1] = {nullptr};
// From info(), obtain the parameters passed from ArkTS. In this example, one ArkTS parameter, arg[0], is obtained.
napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
// Create an ArkTS number as the input parameter of the ArkTS function.
napi_value argv = nullptr;
napi_create_int32(env, 10, &argv);
napi_value result = nullptr;
// Invoke the ArkTS function in the N-API method, save the return value in result, and return result to ArkTS.
napi_call_function(env, nullptr, args[0], 1, &argv, &result);
return result;
}
EXTERN_C_START
// Init() hooks native methods, such as Add and NativeCallArkTS, in exports. exports is the ArkTS object obtained after you import the native capabilities.
static napi_value Init(napi_env env, napi_value exports)
{
// Function description struct. The third parameter "Add" is the N-API method.
// The first parameter "add" is the name of the ArkTS method.
napi_property_descriptor desc[] = {
{ "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "nativeCallArkTS", nullptr, NativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr },
};
// Hook the N-API method to the ArkTS object exports.
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
// Information about the module that loads the data. Record information such as the Init() function and module name.
static napi_module demoModule = {
.nm_version =1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
// When the .so file is opened, the function is automatically called to register the demoModule module.
extern "C" __attribute__((constructor)) void RegisterHelloModule(void)
{
napi_module_register(&demoModule);
}
// entry\src\main\ets\pages\index.ets
import hilog from '@ohos.hilog';
// Import the native capabilities.
import entry from 'libentry.so'
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
// The first button calls the add() method, which uses the N-API Add method to add the two numbers.
Button('ArkTS call C++')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', entry.add(2, 3));
})
// The second button calls the nativeCallArkTS() method, which uses the N-API NativeCallArkTS method to execute the ArkTS function.
Button('C++ call ArkTS')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
let ret = entry.nativeCallArkTS((value)=>{return value * 2;});
hilog.info(0x0000, 'testTag', 'Test NAPI nativeCallArkTS ret = %{public}d', ret);
})
}
.width('100%')
}
.height('100%')
}
}
// entry\src\main\cpp\types\libentry\index.d.ts
// Declare the N-APIs exposed to ArkTS.
export const add: (a: number, b: number) => number;
export const nativeCallArkTS: (a: object) => number;
Development Guidelines
Registration
- To prevent conflicts with symbols in the .so file, add "static" to the function (such as the Init function) corresponding to nm_register_func.
- The entry of module registration, that is, the function name modified by __attribute__((constructor)) (for example, the RegisterHelloModule function), must be unique.
.so Naming Rules
The .so file names must comply with the following rules:
- Each module has a .so file.
- The nm_modname field in napi_module must be the same as the module name. For example, if the module name is hello, name the .so file libhello.so. The sample code for importing the .so file is import hello from 'libhello.so'.
Constraints on JS Object Threads
The ArkCompiler protects JS object threads. Improper use may cause an application crash. Observe the following rules:
- The N-APIs can be used only by JS threads.
- env is bound to a thread and cannot be used across threads. The JS object created by an N-API can be used only in the thread, in which the object is created, that is, the JS object is bound to the env of the thread.
Header File Import
Import napi/native_api.h. Otherwise, an error indicating that the N-API cannot be found will be reported.