鸿蒙最全【坚果派-坚果】Napi入门_libace_napi(2),2024年最新flutter开发桌面应用性能

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

点击 Next 按钮后,项目名称选择NapiHello,点击finish即可

image-20230301145408992

等待项目加载完成,如果大家观察细致的话,会发现,这里面有个不同之处就是多了CPP目录

image-20230301150512194

我么可以点开来看一下

image-20230301151625961

该目录用来存放 cpp 的源码及相关配置文件,各文件说明如下:

  • hello.cppindex.d.ts 文件中声明的方法的 C++ 实现源码。
  • CMakeLists.txt:是cmake用来生成Makefile文件需要的一个描述编译链接的脚本文件。
  • index.d.ts:对 ts 提供的方法声明。
  • package.json:打包的配置文件。

另外 CMakeLists.txt 文件还会在 build-profile.json5 里做配置,代码如下所示:

{
“apiType”: ‘stageMode’,
“buildOption”: {
“externalNativeOptions”: {
“path”: “./src/main/cpp/CMakeLists.txt”,
“arguments”: “”,
“cppFlags”: “”,
}
},
“targets”: [
{
“name”: “default”
},
{
“name”: “ohosTest”,
}
]
}

点击自动化签名,然后运行项目就可以

image-20230301154513142

这个时候我们就可以来看一下代码内容,分析一下了

我们再来看一下index.ets的内容

import hilog from ‘@ohos.hilog’;
import testNapi from ‘libentry.so’

@Entry
@Component
struct Index {
@State message: string = ‘Hello World’

build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message = “值为”+testNapi.add(2, 3)
hilog.info(0x0000, ‘testTag’, ‘Test NAPI 2 + 3 = %{public}d’, testNapi.add(2, 3));

})
}
.width(‘100%’)
}
.height(‘100%’)
}
}

运行之后,我们就可以看到

NAPI项目简述

index.d.ts解读

在 cpp 的 libentry 目录下生成了 index.d.ts 文件,它的源码如下所示:

export const add: (a: number, b: number) => number;

export const 表示导出一个常量以便在其它文件中使用。add 是一个返回类型为 number 的方法,它的参数类为 number 类型。

package.json解读

在 cpp 的 libentry 目录下生成了 package.json 文件,该文件是打包的配置文件,内容如下所示:

{
“name”: “libentry.so”,
“types”: “./index.d.ts”
}

设置 libentry.so 库和 index.d.ts 相关联,便于在 TS 文件中引入 libentry.so 时调用库中的相关方法。

CMakeLists.txt解读

CMake 是一个开源跨平台的构建工具,旨在构建、测试和打包软件,CMake 是 makefile 的上层工具,用于跨平台构建环境,生成可移植的 makefile 并简化自动动手写 makefile 的工作量,在 cpp 目录下默认生成的 CMakeLists.txt 内容如下所示:

the minimum version of CMake.

声明使用 CMAKE 的最小版本号

cmake_minimum_required(VERSION 3.4.1)

声明项目的名称

project(oh_0400_napi)

set命令,格式为set(key value),表示设置key的值为value,其中value可以是路径,也可以是许多文件。

本例中设置NATIVERENDER_ROOT_PATH的值为${CMAKE_CURRENT_SOURCE_DIR}

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

添加项目编译所需要的头文件的目录

include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)

生成目标库文件libentry.so,entry表示最终的库名称,SHARED表示生成的是动态链接库,

hello.cpp表示最终生成的libentry.so中所包含的源码

如果要生成静态链接库,把SHARED该成STATIC即可

add_library(entry SHARED hello.cpp)

把libentry.so链接到libace_napi.z.so上

target_link_libraries(entry PUBLIC libace_napi.z.so)

hello.cpp解读

在 cpp 目录下默认生成的 hello.cpp 文件,源码如下所示:

#include “napi/native_api.h”
#include <js_native_api.h>
#include <js_native_api_types.h>

static napi_value Add(napi_env env, napi_callback_info info)
{
size_t requireArgc = 2;
size_t argc = 2;
napi_value args[2] = {nullptr};

napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);

napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);

double value0;
napi_get_value_double(env, args[0], &value0);

double value1;
napi_get_value_double(env, args[1], &value1);

napi_value sum;
napi_create_double(env, value0 + value1, &sum);

return sum;

}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{ “add”, nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
};

napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END

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 },
};

extern “C” __attribute__((constructor)) void RegisterEntryModule(void)
{
napi_module_register(&demoModule);
}

hello.cpp 的代码不是很复杂,我们可以做如下拆分:

  • 引入头文件

#include “napi/native_api.h”
#include <js_native_api.h>
#include <js_native_api_types.h>

引入头文件,作用和 TS 里的 import 类似,不再详述。

  • 注册napi模块

static napi_module demoModule = {
.nm_version =1,//nm_version:nm版本号,默认值为 1。
.nm_flags = 0,//nm标记符,默认值为 0。
.nm_filename = nullptr,//暂不关注,使用默认值即可。
.nm_register_func = Init,//指定nm的入口函数。
.nm_modname = “entry”,//指定 TS 页面导入的模块名,例如:import testNapi from 'libentry.so' 中的 testNapi
//就是当前的nm_modname。
.nm_priv = ((void*)0),//暂不关注,使用默认值即可。
.reserved = { 0 },//暂不关注,使用默认值即可。
};

extern “C” __attribute__((constructor)) void RegisterEntryModule(void)
{
napi_module_register(&demoModule);
}

定义 NAPI 模块,类型为 napi_module 结构体,各字段说明如下:

  • nm_version:nm版本号,默认值为 1。
  • nm_flags:nm标记符,默认值为 0。
  • nm_filename:暂不关注,使用默认值即可。
  • nm_register_func:指定nm的入口函数。
  • nm_modname:指定 TS 页面导入的模块名,例如:import testNapi from 'libentry.so' 中的 testNapi 就是当前的nm_modname。
  • nm_priv:暂不关注,使用默认值即可。
  • reserved:暂不关注,使用默认值即可。extern "C" 简单理解就是告诉编译器这部分代码按照 C 语言进行编译而不是 C++ 语言编译。__attribute__((constructor)) 声明方法的执行时机,它表示 RegisterEntryModule() 方法在 main() 方法执行前执行, RegisterEntryModule() 方法内调用了 napi_module_register() 方法,该方法是 NAPI 提供的模块注册方法,表示把定义的 demoModule 模块注册到系统中。
  • 方法定义

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{ “add”, nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
};

napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END

Init() 方法内声明了 napi_property_descriptor 结构体,结构体的定义看第一个和第三个参数即可,第一个参数 add 表示应用层 JS 声明的方法,Add 表示 Native C++ 实现的方法,然后调用 NAPI 的 napi_define_properties() 方法将 addAdd 做个映射,最后通过 exports 变量对外导出,实现 JS 端调用 add 方法时进而调用到 C++ 的 Add() 方法。

  • 方法实现

static napi_value Add(napi_env env, napi_callback_info info)
{
// 获取 2 个参数,napi_value是对 JS 类型的封装
size_t requireArgc = 2;
size_t argc = 2;
napi_value args[2] = {nullptr};
// 调用napi_get_cb_info方法,从 info 中读取传递进来的参数放入args里
napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

// 获取参数并校验类型
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);

// 调用napi_get_value_double把 napi_value 类型转换成 C++ 的 double 类型
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);

// 调用napi_create_double方法把 C++类型转换成 napi_value 类型
napi_value sum;
napi_create_double(env, value0 + value1, &sum);

// 返回 napi_value 类型
return sum;

}

Add() 方法注释的很清楚,首先从 napi_callback_info 中读取 napi_value 类型的参数放入到 args 中,然后从 args 中读取参数并把 napi_value 类型转换成 C++ 类型后进行加操作,最后把相加的结果转换成 napi_value 类型并返回。

  • 模块导入

import testNapi from ‘libentry.so’

根据前边的编译配置,cpp 目录下的源码最终打包成了 libentry.so,使用前直接引入即可。

  • 方法调用

import hilog from ‘@ohos.hilog’;
import testNapi from ‘libentry.so’

@Entry
@Component
struct Index {
@State message: string = ‘Hello World’

build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message = “值为”+testNapi.add(2, 3)
hilog.info(0x0000, ‘testTag’, ‘Test NAPI 2 + 3 = %{public}d’, testNapi.add(2, 3));

})
}
.width(‘100%’)
}
.height(‘100%’)
}
}

引入 libentry.so 模块后,就可以直接调用 add() 方法了。

NAPI数据类型

napi_value数据类型

OpenHarmony NAPI 将 ECMAScript 标准中定义的 Boolean、Null、Undefined、Number、BigInt、String、Symbol和 Object 这八种数据类型以及函数对应的 Function 类型统一封装成了 napi_value 类型,它是 JS 数据类型和 C/C++ 数据类型之间的桥梁,napi_value (opens new window)官网说明如下

napi_value 表示 JS 值的不透明指针,在 C/C++ 端要使用 JS 端传递的数据类型,都是通过 NAPI 提供的相关方法把napi_value转换成 C/C++ 类型后再使用,同理当需要把 C/C++的数据传递给 JS 应用层也要通过 NAPI 提供的方法把 C/C++ 端的数据转换成 napi_value 再向上传递。

image-20230302211659632

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

og.csdnimg.cn/img_convert/2ab403e934bf4b9b1e51c3a98787f76f.png)

[外链图片转存中…(img-F5RuJXqm-1715592757443)]
[外链图片转存中…(img-0oSP69JK-1715592757443)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. napi_create_int32: 用于创建一个 int32 类型的 JavaScript 数值。函数原型为:napi_status napi_create_int32(napi_env env, int32_t value, napi_value *result)。其中,env 表示当前的 napi 环境,value 表示要创建的 int32 类型的数值,result 用于存储创建的 JavaScript 数值。 2. napi_get_reference_value: 用于从一个 napi 引用中获取对应的 JavaScript 对象。函数原型为:napi_status napi_get_reference_value(napi_env env, napi_ref ref, napi_value *result)。其中,env 表示当前的 napi 环境,ref 表示要获取的引用对象,result 用于存储获取到的 JavaScript 对象。 3. napi_call_function: 用于调用一个 JavaScript 函数。函数原型为:napi_status napi_call_function(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv, napi_value *result)。其中,env 表示当前的 napi 环境,recv 表示函数的接收者,func 表示要调用的 JavaScript 函数,argc 表示参数个数,argv 表示参数列表,result 用于存储函数调用的结果。 4. NAPI_CALL_BASE: 用于检查 napi 方法返回的错误码,如果出现错误则将错误信息打印到日志中。函数原型为:#define NAPI_CALL_BASE(env, call) \ do { \ napi_status status = (call); \ if (status != napi_ok) { \ const napi_extended_error_info* error_info = 0; \ napi_get_last_error_info((env), &error_info); \ const char* error_message = (error_info->error_message != NULL) ? \ error_info->error_message : "empty error message"; \ printf("NAPI_CALL_BASE failed at %s:%d status=%d, error_message=%s\n", \ __FILE__, __LINE__, status, error_message); \ } \ } while(0) 5. AsyncTask::Schedule: 用于将一个异步任务添加到事件循环中执行。函数原型为:napi_status AsyncTask::Schedule(napi_env env, AsyncTaskExecuteCallback execute, AsyncTaskCompleteCallback complete, void *data, napi_async_context *context)。其中,env 表示当前的 napi 环境,execute 表示异步任务的执行函数,complete 表示异步任务执行完成后的回调函数,data 表示异步任务的数据,context 表示异步任务的上下文。 使用注意: 1. napi_create_int32 和 napi_get_reference_value 的返回值应该被检查,以确保正确地创建和获取 JavaScript 对象。 2. 在调用 napi_call_function 之前,需要确保传入的参数正确,包括函数接收者、函数本身和参数列表。 3. 需要注意异步任务的执行时间,以免阻塞主线程。 4. 在使用 NAPI_CALL_BASE 宏时,需要注意错误信息的输出方式,可以根据需要进行修改。 5. 在使用 AsyncTask::Schedule 时,需要注意异步任务的数据,以确保异步任务可以正确地访问和修改数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值