Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >北向应用集成三方库——NAPI 导出类对象

北向应用集成三方库——NAPI 导出类对象

原创
作者头像
小帅聊鸿蒙
发布于 2024-08-23 12:32:02
发布于 2024-08-23 12:32:02
15100
代码可运行
举报
文章被收录于专栏:鸿蒙开发笔记鸿蒙开发笔记
运行总次数:0
代码可运行

简介

js调用napi的数据,对于简单的数据类型,只需要napi返回对应类型的napi_value数据即可 (详情参照napi数据类型类型与同步调用)。但是对于一些复杂的数据类型(如我们常用c的类对象),是不能直接返回一个napi_value数据的。这时我们需要对这些数据进行一系列操作后将其导出,这样js才能使用导出后的对象。

本文以导出类对象为例来说明napi导出对象的具体过程。

类对象导出的具体过程:

NAPI导出类对象具体实现

这里我们以导出NapiTest类为例说明导出一个类的实现过程

定义NapiTest类以及相关方法

NapiTest类主要实现了接收js设置的数据并将该数据返回到js应用中,具体定义如下(NapiTest.h):

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
class NapiTest {
public:
  NapiTest() : mEnv(nullptr), mRef(nullptr) {
  }
  ~NapiTest();
  
  static napi_value Create(napi_env env, napi_callback_info info);  // 创建NapiTest类的实体,并将实体返回到应用端,该方法为js创建一个类实体,因此需要将该接口对外导出
  static napi_value Init(napi_env env, napi_value exports);         // 初始化js类并设置对应属性并将其导出。

private:
	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);     // 定义js结构体时实际的构建函数
    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;        // 记录生命周期变量
};

将NapiTest定义为js类

  • 在定义js类之前,需要先设置类对外导出的方法
代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  napi_property_descriptor desc[] = {
      { "getMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr,
          napi_default, nullptr },
      { "setMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, 
        napi_default, nullptr },
  }
  • 定义js类
代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  napi_value mConstructor = nullptr;
  if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr,
      sizeof(desc) / sizeof(desc[0]), desc, &mConstructor) != napi_ok) {
      return nullptr;
  }

使用到函数说明:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  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* resu

功能:将c类定义为js的类

参数说明:

  • in env: 调用api的环境
  • in utf8name: c类的名字
  • in length: c类名字的长度,默认自动长度使用NAPI_AUTO_LENGTH
  • in constructor: 处理构造类实例的回调函数
  • in data: 作为回调信息的数据属性传递给构造函数回调的可选数据
  • in property_count: 属性数组参数中的个数
  • in properties: 属性数组
  • out result: 通过类构造函数绑定类实例的napi_value对象

返回:调用成功返回0,失败返回其他

  • 实现js类的构造函数

当js应用通过new方法获取类对象的时候,此时会调用 napi_define_class 中设置 constructor 回调函数,该函数实现方法如下:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  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);
          // 绑定实例类创建NapiTest到导出的对象result
          if (napi_wrap(env, thisVar, reinterpret_cast<void *>(reference),
              NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) {
              return thisVar;
          }
  
          return thisVar;
      }
      
      return undefineVar;
  }

其中NapiTest::Destructo方法是用来释放创建的对象:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
  {
      NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject);
      test->~NapiTest();
  }

使用到函数说明:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  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: 绑定c类实例的js对象
  • in native_object: 类实例对象
  • in finalize_cb: 释放实例对象的回调函数
  • in finalize_hint: 传递给回调函数的数据
  • out result: 绑定js对象的引用

返回:调用成功返回0,失败返回其他

导出js类

  • 创建生命周期(生命周期相关可以参考文档napi生命周期) 在设置类导出前,需要先创建生命周期
代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  if (napi_create_reference(env, mConstructor , 1, &sConstructor_) != napi_ok) {
      return nullptr;
  }

mConstructor 定义js类时返回的代表类的构造函数的数据

sConstructor_ 生命周期变量

  • 将类导出到exports中 将类以属性值的方式导出
代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) !=  napi_ok) {
      return nullptr;
  }

通过以上步骤,我们基本实现了NapiTest这个类的导出。

注意:以上实现都是在类的Init方法中,我们只需要在NAPI注册的接口中调用该Init即可。

创建类的实例对象

js应用除了调用new方法获取类的实例外,我们也可以提供一些方法让js应用获取对应的类的实例,如在我们的NapiTest类中,我们定义了一个Create方法,该方法实现了NapiTest类实例的获取。具体实现如下:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
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的类构造函数可以不实现。

实现NAPI接口的注册

我们已helloworld为列,

  • 新建一个hello.cpp,定义模块
代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  static napi_module demoModule = {
      .nm_version =1,
      .nm_flags = 0,
      .nm_filename = nullptr,
      .nm_register_func = Init,
      .nm_modname = "hello",
      .nm_priv = ((void*)0),
      .reserved = { 0 },
  };
  • 实现模块的Init
代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  EXTERN_C_START
  static napi_value Init(napi_env env, napi_value exports)
  {
    napi_property_descriptor desc[] = {
        { "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr }   // 单独导出 create 方法,js应用可以直接调用Create方法获取类实例
    };
    
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);

    return NapiTest::Init(env, exports);    // 导出类以及类的方法
  }
  EXTERN_C_END
  • 模块注册
代码语言:c
代码运行次数:0
运行
AI代码解释
复制
  // 注册 hello模块
  extern "C" __attribute__((constructor)) void RegisterHelloModule(void)
  {
      napi_module_register(&demoModule);
  }

至此,我们完成了整个napi接口注册以及napi类的导出。

应用调用NAPI实例

导出接口

在使用该NAPI的时候,我们需要在ts文件(路径在\entry\src\main\cpp\types\libentry\index.d.ts),声明以下内容:

代码语言:js
AI代码解释
复制
export const create : () => NapiTest;
export class  NapiTest {
    setMsg(msg: string): void;
    getMsg(): string;
}

该文件申明了NAPI接口中导出的方法和类

应用调用

新建一个helloworld的ETS工程,该工程中包含一个按键,我们可以通过该按键进行数据的在native c中存储和获取

  • 导出napi对应的库(之前NAPI接口生成的库名为libentry.so)
代码语言:js
AI代码解释
复制
  import testNapi from "libentry.so";
  • 定义变量 tt
代码语言:js
AI代码解释
复制
  struct Index {
    @State message: string = 'Hello World'
    @State flag:number = 0
    tt = testNapi.create();
  
    build() {
      Row() {
        Column() {
          Text(this.message)
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
            .onClick(() => {
            })
        }
        .width('100%')
      }
      .height('100%')
    }
  • 在按键中调用对应的接口并输出内容
代码语言:js
AI代码解释
复制
  if (this.falg == 0) {
      this.flag = 2
      this.tt.setMsg("1+1")
  } else {
      this.flag = 0
      this.tt.setMsg("1-1")
  }
  console.info("[NapiTest]:" + this.tt.getMsg() + " = " + this.flag);

通过IDE LOG信息可以查看到,当按多次下按钮时,出现交替以下信息:

代码语言:js
AI代码解释
复制
  02200/JsApp: [NapiTest]1+1 = 2
  02200/JsApp: [NapiTest]1-1 = 0 

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力;
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识;
  • 想要获取更多完整鸿蒙最新学习知识点,可关注B站:码牛课堂;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
北向应用集成三方库——NAPI生命周期
我们都知道,程序的生命周期是指程序从启动,运行到最后的结束的整个过程。生命周期的管理自然是指控制程序的启动,调用以及结束的方法。而NAPI中的生命周期又是怎样的呢?如下图所示:
小帅聊鸿蒙
2024/08/23
1050
北向应用集成三方库——NAPI生命周期
NAPI 类对象导出及其生命周期管理(下)
Node.js Node-API有一组API来获取和设置JavaScript对象的属性。在JavaScript中,属性被表示为一个键和一个值的元组。基本上,Node-API中的所有属性键都可以用以下形式中的任一一种表示:
小帅聊鸿蒙
2024/08/14
1460
NAPI 类对象导出及其生命周期管理(下)
北向应用集成三方库——应用如何调用C/C++三方库
OpenHarmony上的应用一般都是js/ets语言编写的,而js/ets语言是无法直接调用C/c接口的,所以我们应用如果需要调用C/c三方库的话,需要在js/ets和C/c之间建立一个可以互通的桥梁。OpenHarmony系统中提供的napi框架正是这么一座桥梁。
小帅聊鸿蒙
2024/08/24
1420
北向应用集成三方库——应用如何调用C/C++三方库
三方库移植之NAPI开发[2]C/C++与JS的数据类型转
napi_get_cb_info函数在ohos3.2beta3源码foundation/arkui/napi/native_engine/native_api.cpp中
小帅聊鸿蒙
2024/08/12
1390
三方库移植之NAPI开发[2]C/C++与JS的数据类型转
鸿蒙北向应用集成三方库——NAPI数据类型转换与同步调用
在 通过IDE创建工程 的示例代码中,我们使用napi_create_string_utf8函数将C/C++ string转换成NAPI类型——napi_value 。OpenHarmony NAPI将ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,以及函数对应的Function类型,统一封装成napi_value类型 (也称为JS类型)。该类型用于接收应用传递过来的数据以及返回数据给应用。 本文将讲述JS类型和C/C++数据类型之间的转换 。
小帅聊鸿蒙
2024/08/21
3550
鸿蒙北向应用集成三方库——NAPI数据类型转换与同步调用
鸿蒙(HarmonyOS)安全和高效的使用N-API开发Native模块
N-API 是 Node.js Addon Programming Interface 的缩写,是 Node.js 提供的一组 C++ API,封装了 V8 引擎 的能力,用于编写 Node.js 的 Native 扩展模块。通过 N-API,开发者可以使用 C++ 编写高性能的 Node.js 模块,同时保持与 Node.js 的兼容性。
小帅聊鸿蒙
2024/07/24
4050
鸿蒙(HarmonyOS)安全和高效的使用N-API开发Native模块
ArkUI实战开发-NAPI异步编程
笔者在前 5 小节里讲述了在 OpenHarmony 上通过 NAPI 的方式实现了 JS 调用 C++的能力,但是这些实现都是同步的,本节笔者简单介绍一下 NAPI 的异步实现。
小帅聊鸿蒙
2024/10/11
1510
ArkUI实战开发-NAPI异步编程
三方库移植之NAPI开发[1]—Hello OpenHarmony NAPI
直接在OpenHarmony源码根目录创建子系统文件夹,取名mysubsys。并在目录下添加子系统的构建配置文件ohos.build
小帅聊鸿蒙
2024/08/12
4760
三方库移植之NAPI开发[1]—Hello OpenHarmony NAPI
ArkUI实战开发-NAPI项目
上节笔者简单介绍了使用 DevEco Studio 创建的默认 NAPI 工程结构,本节笔者简单介绍一下 NAPI 工程下 cpp 目录的源码部分。
小帅聊鸿蒙
2024/10/09
1320
ArkUI实战开发-NAPI项目
napi系列学习基础篇——如何通过DevEco Studio开发一个NAPI工程
NAPI(Native API)是OpenHarmony系统中的一套原生模块扩展开发框架,它基于Node.js N-API规范开发,为开发者提供了JavaScript与C/C++模块之间相互调用的交互能力。如下图所示:
小帅聊鸿蒙
2024/08/27
4600
napi系列学习基础篇——如何通过DevEco Studio开发一个NAPI工程
三方库移植之NAPI开发(3)通过IDE开发NAPI工程
将默认的hello.cpp文件重命名为hellonapi.cpp,选中右键选中重构重命名。
小帅聊鸿蒙
2024/08/13
1740
三方库移植之NAPI开发(3)通过IDE开发NAPI工程
从c++ addon看napi的实现
Node.js的napi极大地方便了c++ addon的编写,使得用户不再那么需要面对复杂的v8。本文通过一个例子来分析一下napi的使用和napi到底做了什么。
theanarkh
2021/05/08
2.2K0
纯血鸿蒙APP实战开发——NAPI封装ArkTS接口案例
部分应用的主要开发语言为C/C++,但是HarmonyOS的部分接口仅以ArkTS的形式暴露,因此需要将ArkTS的接口封装为Native接口。本例以DocumentViewPicker的Select方法为例,提供了Napi封装ArkTS
小帅聊鸿蒙
2025/01/22
1680
纯血鸿蒙APP实战开发——NAPI封装ArkTS接口案例
ArkUI实战开发-NAPI方法扩展
在前 3 小结笔者简单介绍了 NAPI 工程并对生成的源码进行了简单介绍,本节笔者在前 3 小节的基础上对 NAPI 工程做个扩展,再额外添加一个计算 MD5 的方法 md5()。
小帅聊鸿蒙
2024/10/10
1150
ArkUI实战开发-NAPI方法扩展
HarmonyOS 开发实践 —— 基于JSVM创建引擎执行JS代码并销毁
通过JSVM,可以在应用运行期间直接执行一段动态加载的JS代码。也可以选择将一些对性能、底层系统调用有较高要求的核心功能用C/C++实现并将C++方法注册到JS侧,在JS代码中直接调用,提高应用的执行效率。
小帅聊鸿蒙
2024/11/29
2390
HarmonyOS 开发实践 —— 基于JSVM创建引擎执行JS代码并销毁
OpenHarmony集成OCR三方库实现文字提取
Tesseract(Apache 2.0 License)是一个可以进行图像OCR识别的C++库,可以跨平台运行 。本样例基于Tesseract库进行适配,使其可以运行在OpenAtom OpenHarmony(以下简称“OpenHarmony”)上,并新增N-API接口供上层应用调用,这样上层应用就可以使用Tesseract提供的相关功能。
小帅聊鸿蒙
2025/04/24
1280
OpenHarmony集成OCR三方库实现文字提取
北向应用集成三方库——Napi接口封装工具aki
AKI (Alpha Kernel Interacting) 是一款边界性编程体验友好的ArkTs FFI开发框架,针对OpenHarmony Native开发提供JS与C/C++跨语言访问场景解决方案。支持极简语法糖使用方式,一行代码完成JS与C/C++的无障碍跨语言互调,所键即所得。
小帅聊鸿蒙
2024/08/25
4391
北向应用集成三方库——Napi接口封装工具aki
HarmonyOS 开发实践——基于napi_load_module_with_info实现的napi调用arkts的接口
napi_load_module_with_info接口的功能是进行模块的加载,当模块加载出来之后,可以使用函数napi_get_property获取模块导出的变量,也可以使用napi_get_named_property获取模块导出的函数,该函数可以在新创建的ArkTs基础运行时环境中使用
小帅聊鸿蒙
2024/11/13
1890
HarmonyOS 开发实践——基于Napi调用ArkTS/系统接口
场景一:系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;
小帅聊鸿蒙
2024/11/04
4090
HarmonyOS 开发实践——基于Napi调用ArkTS/系统接口
理解nodejs插件的加载原理并使用n-api编写你的第一个nodejs插件
nodejs拓展本质是一个动态链接库,写完编译后,生成一个.node文件。我们在nodejs里直接require使用,nodejs会为我们处理这一切。下面我们按照文档写一个拓展并通过nodejs14源码了解他的原理(ubuntu18.4)。 首先建立一个test.cc文件
theanarkh
2020/06/05
2.7K0
推荐阅读
相关推荐
北向应用集成三方库——NAPI生命周期
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验