Detailed explanation of Openharmony application NAPI - Basics

What are NAPIs?

A simple understanding is that in Openharmony, it is a framework that implements the interaction between the upper js or ets application and the underlying C/C++.

Official explanation in Openharmony: NAPI (Native API) component is a set of native module extension development framework with external interface developed based on Node.js N-API specification. There is also NAPI, which is suitable for encapsulating IO, CPU-intensive, OS bottom layer and other capabilities and exposing JS interfaces to the outside world. Through NAPI, JS and C/C++ codes can access each other. We can build modules such as network communication, serial port access, multimedia decoding, and sensor data collection through the NAPI interface.

Now, taking the Openharmon V3.1-Release version as an example, extract the code example and divide it into two parts for analysis.

Facing upper-level js or ets interface

1. Define the interface

Interface description files end with the .d.ts suffix in Openharmony.

In the @ohos.sendfile.d.ts file of the Distributed File Service (//foundation/filemanagement/dfs_service)

import {AsyncCallback, Callback} from "./basic";

declare namespace SendFile {
  // 异步接口----回调方式
  function sendFile(deviceId: string, sourPath: Array<string>, destPath: Array<string>, fileCount: number, callback: AsyncCallback<number>);
  // 异步接口----Promise方式
  function sendFile(deviceId: string, sourPath: Array<string>, destPath: Array<string>, fileCount: number): Promise<number>;
  // 异步接口----回调方式
  function on(type: 'sendFinished' | 'receiveFinished', callback: AsyncCallback<TransResult>): void;
  // 同步接口
  function off(type: 'sendFinished' | 'receiveFinished'): void;
}

export default SendFile;

The above provides four JS interfaces for application developers, which are divided into two categories: synchronous interfaces and asynchronous interfaces. Asynchronous interfaces are further divided into callback methods and Promise.

2. The JS code of the App application developer can be called directly by simply importing the module.

// 同步调用
import myapp1 from "@ohos.SendFile"
var result = myapp1.off(/* 参数自填*/);

// 异步调用
/** call back 方式的异步调用 */
myapp1.sendFile("xxxx", sourPath, destPath, 4, function (err, ret) {
    ......
});
/** Promise 方式的异步调用 */
var  promiseObj = myapp1.sendFile("xxxx", sourPath, destPath, 4).then(data => {
    ......
}).catch(error => {
    ......
});

3. Notes on d.ts

1. When customizing the d.ts interface file, you need to put this file into the directory where the Openharmony sdk is located in the application development tool (DevEco Studio). For example, on the author's local machine it is D:\Program\Huawei\OpenHarmony3.1 \Sdk\ets\3.1.5.5\api and D:\Program\Huawei\OpenHarmony3.1\Sdk\js\3.1.5.5\api are placed in different directories according to the application caller (js or ets).

2. Openharmony already has a d.ts interface file. According to the @since 9 mentioned in the comments in the file, there is no need to import dts separately after corresponding to the corresponding imported sdk version.

C++-oriented interface implementation

1. Module registration and method name mapping

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value SendFileExport(napi_env env, napi_value exports)
{
    const char className[] = "SendFile";
    static napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("sendFile", JsSendFile),
        DECLARE_NAPI_FUNCTION("on", JsOn),
        DECLARE_NAPI_FUNCTION("off", JsOff),
    };
    napi_value sendFileClass = nullptr;

    napi_define_class(env, className, sizeof(className), JsConstructor, nullptr,
        sizeof(desc) / sizeof(desc[0]), desc, &sendFileClass);

    napi_set_named_property(env, exports, "SendFile", sendFileClass);

    SendFile::RegisterCallback();
    return exports;
}
// 模块注册
NAPI_MODULE(sendfile, SendFileExport)
  • Module registration and method name mapping are completed through the two macros NAPI_MODULE and DECLARE_NAPI_FUNCTION respectively.

  • Here dts defines two sendFile interfaces, why do you only need to define and map one?

Because the external interface name of sendFile is the same for js or ets, when implemented in C++, it is judged which function to call by js or ets based on the number or parameter type passed in.

2. Synchronous interface

According to the mapping, the corresponding C++ implementation of synchronization interface off is JsOff.

  • function declaration

Each mapped function must have parameters napi_env env, napi_callback_info cbinfo, and the return value is napi_value.

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
......
}

In order to implement js or ets calls, the NAPI framework needs to solve the following problems: data transfer and conversion

The input parameters passed in by js/ets and the returned results need to be converted into data types that can be operated by C/C++ code. The NAPI framework introduces an intermediate data type to correspond to the upper-layer js/ets and C/C++ types respectively. , and the operation methods of data types.

  • Get input

napi_get_cb_info passes in the js parameters from the cbinfo parameter. The following is a function description.

// napi_get_cb_info从napi_callback_info类型的参数中得到回调的参数等数据
napi_status napi_get_cb_info(
  napi_env env,
  napi_callback_info cbinfo, // 传入回调函数的回调信息
  size_t *argc,             // 作为入参传入`argv`数组的大小,并将接收实际的参数个数
  napi_value *argv,         // 存放参数的buffer
  napi_value *this_arg,     // Javascript中的`this`
  void** data               // 接收数据指针
);

argc is the number of parameters passed in, and argv is the parameter array passed in. At this time, the types of parameters are all napi_value.

Obtain the actual type of the input parameter through the napi_typeof method

// napi_typeof获取指定对象的类型
napi_status napi_typeof(
  napi_env env,
  napi_value value,         // 将要获取类型的Javascript值
  napi_valuetype *result    // Javascript值的类型
);

According to the description of d.ts, off only passes in one parameter, and the parameter type is a string.

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
    size_t requireArgc = 1;
...
    NAPI_ASSERT(env, argc == requireArgc, "requires 1 parameter"); //参数个数为1 
    napi_valuetype eventValueType = napi_undefined;
	napi_typeof(env, argv[0], &eventValueType);
	NAPI_ASSERT(env, eventValueType == napi_string, "type mismatch for parameter 1");//参数类型为napi_string,即为NAPI中定义一种字符串
...
}
  • Type conversion to a type recognized by C/C++

The napi_get_value_string_utf8 method converts napi_string to char*

napi_status napi_get_value_string_utf8(napi_env env,
  napi_value value,
  char* buf,
  size_t bufsize,
  size_t* result);
// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
...
    char* type = nullptr;
    size_t typeLen = 0;
    napi_get_value_string_utf8(env, argv[0], nullptr, 0, &typeLen);

    NAPI_ASSERT(env, typeLen > 0, "typeLen == 0");
    type = new char[typeLen + 1];

    napi_get_value_string_utf8(env, argv[0], type, typeLen + 1, &typeLen);
...
}
  • return value

C++ has no return value, and JsOff will return nullptr at this time.

The NAPI framework does not have nullptr, and nullptr is converted to napi_undefined through napi_get_undefined.

napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
...
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

follow-up more exciting

1. Detailed explanation of Openharmony application NAPI - advanced chapter

Guess you like

Origin blog.csdn.net/procedurecode/article/details/128740926