4. 样例工程源码分析
- 工程的模板是
Native C++
,模型是Stage
。 - 源码分析次要围绕以下几个文件
4.1. NAPI导出对象和生命周期治理具体实现
4.1.1. 定义NapiTest类及办法
Napi.h文件内容如下:
#ifndef __NAPI_TEST_H__#define __NAPI_TEST_H__#include "napi/native_api.h"#include <js_native_api_types.h>#include <iostream>#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__ */
4.1.1.1 napi_value
- Node.js Node-API的值用napi_value类型示意。
OpenHarmony NAPI将ECMAScript规范中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,以及函数对应的Function类型,对立封装成napi_value类型,下文中表述为JS类型,用于接管ArkUI利用传递过去的数据及返回数据给ArkUI利用。 - 这是一个不通明的指针,用于示意JavaScript值。
4.1.1.2 napi_ref
- 这是用来援用napi_value的形象。这容许用户治理JavaScript值的生命周期,包含显式地定义它们的最小生命周期。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap...
4.1.1.3 napi_env
- napi_env用于示意上下文,底层的Node-API实现能够应用该上下文长久放弃VM-specific的状态。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap...
4.1.2 将NapiTest类定义为js类
4.1.2.1在定义js类之前,须要先设置js类对外导出的办法
// 在定义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 } };
4.1.2.1.1 napi_property_descriptor
参考 https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap...
Node.js Node-API有一组API来获取和设置JavaScript对象的属性。在JavaScript中,属性被示意为一个键和一个值的元组。基本上,Node-API中的所有属性键都能够用以下模式中的任一一种示意:
- Named:一个简略的UTF-8编码的字符串
- Integer-Indexed:索引值,由uint32_t示意
- JavaScript value:在Node-API中通过napi_value示意。它能够是一个napi_value,示意字符串、数字或符号。
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;
参数解析:
- utf8name:在定义js类之前设置的js类对外导出的办法名字,编码为UTF8。必须为该属性提供utf8name或name中的一个。(utf8name和name其中一个必须是NULL)
- name:可选的napi_value,指向一个JavaScript字符串或符号,用作属性的键。必须为该属性提供utf8name或name中的一个。
- method:将属性描述符对象的value属性设置为method示意的JavaScript函数。如果传入这个参数,将value、getter和setter设置为NULL(因为这些成员不会被应用)。
- attributes:与特定属性相关联的属性。
- data:调用函数时传递给method、getter和setter的callback data。
4.1.2.2 定义与C++类绝对应的JavaScript类
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; }
4.1.2.2.1 napi_define_class
napi_define_class
函数阐明:
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap...
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)
4.1.2.3 实现js类的构造函数
当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<NapiTest*>(nativeObject); test->~NapiTest();}
NapiTest::Destructo办法是用来开释创立的对象:
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize){ // 类析构函数,开释资源 NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject); test->~NapiTest();}
4.1.2.3.1 napi_wrap
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对象,并关联对应的生命周期
参数阐明:
- [in] env: 调用api的环境
- [in] js_object: 绑定native_object的js对象
- [in] native_object: C++类实例对象
- [in] finalize_cb: 开释实例对象的回调函数
- [in] finalize_hint: 传递给回调函数的数据
- [out] result: 绑定js对象的援用
返回:调用胜利返回0,失败返回其余
4.1.2.3.2 napi_get_cb_info
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)
参数阐明:
- [in] env: 传入接口调用者的环境,蕴含js引擎等,由框架提供,默认状况下间接传入即可
- [in] cbinfo: napi_callback_info对象,上下文的信息
- [in-out] argc: argv数组的长度。若napi_callback_info中理论蕴含的参数的个数大于申请的数量argc,将只复制argc的值所指定数量的参数只argv中。若理论的参数个数小于申请的数量,将复制全副的参数,数组多余的空间用空值填充,并将参数理论长度写入argc。
- [out] argv: 用于接管参数列表
- [out] this_arg: 用于接管this对象
- [out] data: NAPI的上下文数据 返回值:返回napi_ok示意转换胜利,其余值失败。上面的返回napi_status办法一样。
4.1.3 导出js类
// 创立生命周期,初始援用计数设为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; }
4.1.3.1 在设置js类导出前,须要先创立生命周期
if (napi_create_reference(env, constructor , 1, &sConstructor_) != napi_ok) { return nullptr;}
- constructor 定义js类时返回的代表类的构造函数的数据
- sConstructor_ 生命周期变量
4.1.3.1.1 napi_create_reference
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就是胜利的.
4.1.3.2 将生命周期变量作为导出对象的传入属性,并将js类导出到exports中
// 设置constructor对象相干属性并绑定到导出变量exportsif (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) { return nullptr;}
4.1.3.2.1 napi_set_named_property
为给定对象的属性设置一个名称。
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是胜利的
4.1.3.3 设置导出对象的属性
hello.cpp中
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
4.1.3.3.1 napi_define_properties
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap...
napi_status napi_define_properties(napi_env env, napi_value object, size_t property_count, const napi_property_descriptor* properties);
作用:批量的向给定Object中定义属性
- [in] env: 调用api的环境
- [in] object: js对象相干属性的导出变量
- [in] property_count: 属性数组中的元素数
- [in] properties: 属性数组
4.1.4 创立类的实例对象
- ArkTS利用除了调用new办法获取类的实例外,咱们也能够提供一些办法让ArkTS利用获取对应的类的实例,如在咱们的NapiTest类中,定义了一个Create办法,该办法实现了NapiTest类实例的获取。具体实现如下:
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的代码够能够不写)
4.1.4.1 napi_get_reference_value
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap...
函数阐明:
NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, napi_ref ref, napi_value* result);
- 作用:获取与reference相关联的js对象
- [in] env: 调用API的环境
- [in] ref: 生命周期治理的变量
- [out] result: 对象援用的reference.
4.1.4.2 napi_new_instance
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap...
napi_status napi_new_instance(napi_env env, napi_value cons, size_t argc, napi_value* argv, napi_value* result)
- 作用:通过给定的构造函数,构建一个对象
- [in] env: 调用API的环境
- [in] cons: napi_value示意要作为结构函数调用的 JavaScript 函数
- [in] argc: argv 数组中的元素计数
- [in] argv: JavaScript 值数组,示意构造函数的参数napi_value。
- [out] result: napi_value示意返回的 JavaScript 对象
4.2 index.d.ts申明文件编写
应用NAPI框架代码生成工具,能够依据.h生成.d.ts
https://gitee.com/openharmony/napi_generator/blob/master/docs...
export const create : () => NapiTest;export class NapiTest { setMsg(msg: string): void; getMsg(): string;}
也能够写成
export class NapiTest { create(); setMsg(msg: string): void; getMsg(): string;}
4.3 CMakeLists.txt文件
# 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 )
4.4 index.ets文件
// 让IDE不查看文件语法// @ts-nocheck import testNapi from "libentry.so";@Entry@Componentstruct 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%') }}
知识点附送
napi接口名称
https://gitee.com/openharmony/docs/blob/master/zh-cn/applicat...