工程的模板是Native C++
,模型是Stage
。
源码剖析主要围绕以下几个文件
#ifndef __NAPI_TEST_H__
#define __NAPI_TEST_H__
#include "napi/native_api.h"
#include
#include
#define NAPI_CLASS_NAME "NapiTestClass"
class NapiTest {
public:
NapiTest() : mEnv(nullptr), mRef(nullptr) {
}
NapiTest(napi_env env) : mEnv(env), mRef(nullptr){
}
~NapiTest();
// 创建NapiTest类的实体,并将实体返回到应用端,该方法为js创建一个类实体,因此需要将该接口对外导出
static napi_value Create(napi_env env, napi_callback_info info);
// 初始化js类并设置对应属性并将其导出
static napi_value Init(napi_env env, napi_value exports);
private:
// 设置数据,此方法给到js直接调用,因此需要将该接口对外导出
static napi_value SetMsg(napi_env env, napi_callback_info info);
// 获取数据,此方法给到js直接调用,因此需要将该接口对外导出
static napi_value GetMsg(napi_env env, napi_callback_info info);
// 定义js结构体时实际的构建函数
static napi_value Constructor(napi_env env, napi_callback_info info);
// 释放资源的函数(类似类的析构函数)
static void Destructor(napi_env env, void *nativeObject, void *finalize);
// 生命周期变量
static napi_ref sConstructor_;
// 设置和获取数据的变量
static std::string _msg;
// 记录环境变量
napi_env mEnv = nullptr;
// 记录生命周期变量
napi_ref mRef = nullptr;
};
#endif /* __NAPI_TEST_H__ */
Node.js Node-API的值用napi_value类型表示。
OpenHarmony NAPI将ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,以及函数对应的Function类型,统一封装成napi_value类型,下文中表述为JS类型,用于接收ArkUI应用传递过来的数据及返回数据给ArkUI应用。
这是一个不透明的指针,用于表示JavaScript值。
这是用来引用napi_value的抽象。这允许用户管理JavaScript值的生命周期,包括显式地定义它们的最小生命周期。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_ref
napi_env用于表示上下文,底层的Node-API实现可以使用该上下文持久保持VM-specific的状态。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_env
// 在定义js类之前,需要先设置类对外导出的方法
napi_property_descriptor desc[] = {
{ "getMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "setMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr }
};
参考 https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_property_descriptor
Node.js Node-API有一组API来获取和设置JavaScript对象的属性。在JavaScript中,属性被表示为一个键和一个值的元组。基本上,Node-API中的所有属性键都可以用以下形式中的任一一种表示:
typedef struct {
// utf8name和name其中一个必须是NULL
const char* utf8name;
napi_value name;
napi_callback method;
napi_callback getter;
napi_callback setter;
napi_value value;
napi_property_attributes attributes;
void* data;
} napi_property_descriptor;
参数解析:
napi_value constructor = nullptr;
// 定义与C++类相对应的JavaScript类
if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]),
desc, &constructor) != napi_ok) {
// "!="用来检查两个操作数的值是否相等,如果不相等则条件为真
return nullptr;
}
napi_define_class
函数说明:
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_class
napi_status napi_define_class(napi_env env,
const char* utf8name,
size_t length,
napi_callback constructor,
void* data,
size_t property_count,
const napi_property_descriptor* properties,
napi_value* result);
功能:定义与C ++ 类相对应的JavaScript类。
参数说明:
[in] env: 调用api的环境
[in] utf8name: C ++ 类的名称
[in] length: C ++ 类的名称的长度,默认自动长度使用NAPI_AUTO_LENGTH
[in] constructor: 处理C ++ 类实例构造的回调函数 (因为Constructor函数被napi_define_class调用了)。在导出C ++ 类对象时,这个函数必须是带有napi_callback签名(Constructor函数有napi_callback签名是指要满足typedef napi_value (*napi_callback)(napi_env, napi_callback_info);的形式)的静态成员。不能使用c ++ 的类构造函数。
[in] data: 作为回调信息的数据属性传递给构造函数回调的可选数据
[in] property_count: 属性数组中参数的个数
[in] properties: 属性数组,具体看代码中napi_property_descriptor部分
[out] result: 通过类构造函数绑定类实例的napi_value对象
返回:如果API调用成功返回napi_ok。
JS构造函数
如果一个js函数被使用new操作符来调用了,那么这个函数就称之为js构造函数
C++类回调函数
我们调用别人的API叫call,调用的第三方API调用我们的函数叫回调(callback)
当ArkTS应用在js端通过new方法获取类对象的时候,此时会调用 napi_define_class 中设置的 constructor 回调函数,该函数实现方法如下:
napi_value NapiTest::Constructor(napi_env env, napi_callback_info info)
{
napi_value undefineVar = nullptr, thisVar = nullptr;
napi_get_undefined(env, &undefineVar);
// 获取传入的参数对象,对象不为空,根据该参数创建实例并并绑定到该对象
if (napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr) == napi_ok && thisVar != nullptr) {
// 创建NapiTest 实例
NapiTest *reference = new NapiTest(env);
// 绑定实例到对象并获取对象的生命周期
if (napi_wrap(env, thisVar, reinterpret_cast<void *>(reference), NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) {
return thisVar;
}
return thisVar;
}
return undefineVar;
}
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
// 释放资源
NapiTest *test = reinterpret_cast
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
// 类析构函数,释放资源
NapiTest *test = reinterpret_cast
napi_status napi_wrap(napi_env env,
napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
void* finalize_hint,
napi_ref* result);
功能:将C++类实例绑定到js对象,并关联对应的生命周期
参数说明:
返回:调用成功返回0,失败返回其他
NAPI提供了napi_get_cb_info()方法可从napi_callback_info中获取参数列表、this及其他数据。这个方法在constructor回调函数中使用,从给定的回调信息中检索有关调用的详细信息,如参数和This指针。
napi_status napi_get_cb_info(napi_env env,
napi_callback_info cbinfo,
size_t* argc,
napi_value* argv,
napi_value* this_arg,
void** data)
参数说明:
// 创建生命周期,初始引用计数设为1
if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) {
return nullptr;
}
// 设置NapiTest对象相关属性并绑定到导出变量exports
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {
return nullptr;
}
if (napi_create_reference(env, constructor , 1, &sConstructor_) != napi_ok) {
return nullptr;
}
napi_create_reference
为对象创建一个reference,以延长其生命周期。调用者需要自己管理reference生命周期。
napi_create_reference函数说明:
NAPI_EXTERN napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result);
功能:通过引用对象创建新的生命周期引用对象
[in] env: 调用 API 的环境
[in] value: napi_value表示我们要引用的对象
[in] initial_refcount: 生命周期变量的初始引用计数
[out] result: 新建的生命周期引用对象
返回 napi_ok 这个API就是成功的.
// 设置constructor对象相关属性并绑定到导出变量exports
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {
return nullptr;
}
为给定对象的属性设置一个名称。
napi_status napi_set_named_property(napi_env env,
napi_value object,
const char* utf8Name,
napi_value value);
[in] env: 调用API的环境
[in] object: NapiTest对象相关属性要绑定的属性值
[in] utf8Name: js类的名称
[in] value: 要引用的对象
返回 napi_ok 则这个API是成功的
hello.cpp中
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_properties
napi_status napi_define_properties(napi_env env,
napi_value object,
size_t property_count,
const napi_property_descriptor* properties);
作用:批量的向给定Object中定义属性
napi_value NapiTest::Create(napi_env env, napi_callback_info info) {
napi_status status;
napi_value constructor = nullptr, result = nullptr;
// 获取生命周期变量
status = napi_get_reference_value(env, sConstructor_, &constructor);
// 创建生命周期内的实例对象并将其返回
status = napi_new_instance(env, constructor, 0, nullptr, &result);
auto napiTest = new NapiTest();
// 绑定实例类创建NapiTest到导出的对象result
if (napi_wrap(env, result, reinterpret_cast<void *>(napiTest), Destructor,
nullptr, &(napiTest->mRef)) == napi_ok) {
return result;
}
return nullptr;
}
在napi接口的注册中将该方法以接口的方式导出,应用层就可以直接调用该接口并获取到该类的实例对。
特别说明:如果单独实现了一个类实例获取的方法,那么js的类构造函数可以不实现(也就是定义js结构体时实际的构建函数
Constructor及释放资源的函数
Destructor的代码够可以不写)
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_get_reference_value
函数说明:
NAPI_EXTERN napi_status napi_get_reference_value(napi_env env,
napi_ref ref,
napi_value* result);
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_new_instance
napi_status napi_new_instance(napi_env env,
napi_value cons,
size_t argc,
napi_value* argv,
napi_value* result)
使用NAPI框架代码生成工具,可以根据.h生成.d.ts
https://gitee.com/openharmony/napi_generator/blob/master/docs/INSTRUCTION_ZH.md
export const create : () => NapiTest;
export class NapiTest {
setMsg(msg: string): void;
getMsg(): string;
}
也可以写成
export class NapiTest {
create();
setMsg(msg: string): void;
getMsg(): string;
}
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(ObjectWrapTest)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
# 头文件路径
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
# 动态库源文件
add_library(entry SHARED hello.cpp NapiTest.cpp)
# 依赖libace_napi.z.so动态库
target_link_libraries(entry PUBLIC libace_napi.z.so )
// 让IDE不检查文件语法
// @ts-nocheck
import testNapi from "libentry.so";
@Entry
@Component
struct Index {
@State message: string = '导出对象'
@State nativePointer:number = 0
// 创建对象tt
tt = testNapi.create();
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
console.info("[NapiTest] Test NAPI 2 + 3 = " + testNapi.add(2, 3));
try{
if (this.nativePointer == 0) {
// log打印,在程序中添加 log
console.info("[NapiTest] Test NAPI add(2, 3) 1");
this.nativePointer = testNapi.add(2, 3)
console.info("[NapiTest] Test NAPI add(2, 3) 2");
this.tt.setMsg("2+3")
console.info("[NapiTest] Test NAPI add(2, 3) 3");
} else {
console.info("[NapiTest] Test NAPI add(0, 0) 1");
this.nativePointer = testNapi.add(0, 0)
console.info("[NapiTest] Test NAPI add(0, 0) 2");
this.tt.setMsg("4+5")
console.info("[NapiTest] Test NAPI add(0, 0) 3");
}
} catch(e) {
console.info("[NapiTest]Test NAPI error" + JSON.stringify(e));
}
console.info("[NapiTest]Test NAPI " + this.tt.getMsg() + " = " + this.nativePointer);
})
}
.width('100%')
}
.height('100%')
}
}
https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/native-lib/third_party_napi/napi.md
https://gitee.com/jiangtao92/node-api-test-case
全部0条评论
快来发表一下你的评论吧 !