上节笔者简单介绍了使用 DevEco Studio 创建的默认 NAPI 工程结构,本节笔者简单介绍一下 NAPI 工程下 cpp 目录的源码部分。
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)
CMakeLists.txt 内容注释比较详细,笔者就不一一叙述了,更多详细用法读者可自行查阅官网。
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_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);
}
定义 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()
方法执行前执行,简单理解就是当前 CPP 文件被编译成动态链接库 so 后,在调用 dlopen()
方法加载该库时会先执行 RegisterEntryModule()
方法。该方法内又调用了 napi_module_register()
方法,napi_module_register()
方法是 NAPI 提供的模块注册方法,表示把定义的 demoModule 模块注册到 JS 引擎中。
- 方法定义
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 表示 C++
实现的方法,然后调用 NAPI 的 napi_define_properties() 方法将 add
和 Add
这俩方法做做个映射,最后通过 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 testNapi from 'libentry.so'
@Entry @Component struct Index {
@State message: string = 'Hello,OpenHarmony'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(25)
.fontWeight(FontWeight.Bold)
.onClick(() => {
var result = testNapi.add(2, 3);
this.message = "Hello,OpenHarmony, value: " + result;
console.log(this.message);
})
}
.width('100%')
}
.height('100%')
}
}
引入 libentry.so 模块后,就可以直接调用 add()
方法了。
小结
本节简单介绍了默认创建的 NAPI 工程源码,读者有个大致印象即可,下节笔者将简单介绍一下 NAPI 提供的相关方法,便于后续编写 C++ 代码或者移植三方库。
为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05
《鸿蒙开发学习手册》:
如何快速入门:https://qr21.cn/FV7h05
- 基本概念
- 构建第一个ArkTS应用
- ……
开发基础知识:https://qr21.cn/FV7h05
- 应用基础知识
- 配置文件
- 应用数据管理
- 应用安全管理
- 应用隐私保护
- 三方应用调用管控机制
- 资源分类与访问
- 学习ArkTS语言
- ……
基于ArkTS 开发:https://qr21.cn/FV7h05
- Ability开发
- UI开发
- 公共事件与通知
- 窗口管理
- 媒体
- 安全
- 网络与链接
- 电话服务
- 数据管理
- 后台任务(Background Task)管理
- 设备管理
- 设备使用信息统计
- DFX
- 国际化开发
- 折叠屏系列
- ……
鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH
鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH
1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向