


在鸿蒙(HarmonyOS)应用开发中,ArkTS/JS 以高效便捷占据主流,但面对音视频编解码、高性能计算等场景时,编译型语言 C/C++ 的性能优势不可替代。此时,NAPI(Native API) 便成为连接 ArkTS 与原生世界的桥梁 —— 它是鸿蒙生态中实现跨语言交互的标准化框架,让两种语言既能各司其职,又能无缝协作。
NAPI 并非鸿蒙独创,其设计参考了 Node.js 的 N-API 规范,本质是一套稳定的二进制接口(ABI)。它的核心角色是 “翻译官”: 让 ArkTS 代码能像调用自身函数一样调用 C/C++ 原生方法; 让原生代码能反向触发 ArkTS 的回调逻辑; 屏蔽不同语言的底层差异(如数据类型、内存管理),实现 “一次开发,多端适配”。 在鸿蒙 6.0(API20)中,NAPI 的价值尤为突出:既保留 ArkTS 的开发效率,又能复用 C/C++ 原生库(如 OpenCV、FFmpeg),同时适配手机、车机等全场景设备。
NAPI 的跨语言交互并非 “黑盒”,其底层围绕上下文管理、数据转换、函数调用三大支柱展开:
任何 NAPI 调用都依赖Env(环境对象),它是跨语言通信的 “全局状态管理器”: 存储当前调用的上下文(如 ArkTS 引擎状态、内存记录); 提供内存管理接口(原生代码创建的对象需依附于 Env 的生命周期); 保证线程安全(Env 与线程一一绑定,不可跨线程共享)。 简单来说,Env 是 NAPI 的 “操作地基”,所有交互都必须在 Env 的框架内进行。
ArkTS 与 C/C++ 的类型体系差异巨大(如 ArkTS 的Number对应 C++ 的int/double),NAPI 通过统一容器 + 类型转换接口解决这一问题: 统一容器:用napi_value封装所有 ArkTS 数据(数字、字符串、函数等在原生代码中均以napi_value表示); 类型转换:通过napi_get_value_double(ArkTS Number 转 C++ double)、napi_create_string_utf8(C++ 字符串转 ArkTS String)等接口,实现数据的双向映射。 例如,将 ArkTS 传入的两个数字相加,原生代码需先通过napi_get_value_double提取数值,计算后再通过napi_create_double返回结果。
NAPI 支持两种核心调用模式,覆盖不同业务场景:

一定要选择这个

然后点击创建即可

理论看似简单,但实际开发中容易踩中 “名称不匹配”“配置错误” 等坑 —— 以下是基于真实开发的避坑指南:
NAPI 开发的核心原则是 “一处命名,处处统一”,否则会出现 “模块找不到” 的问题: CMake 的project名、add_library名需与原生代码的nm_modname一致; ArkTS 导入的库名需与 CMake 生成的动态库名一致(如 CMake 生成libentry.so,ArkTS 需导入’libentry.so’); types目录下的声明文件需与模块名对应(如types/libentry对应模块entry)。 踩坑案例:若 CMake 写project(demoModule),但原生代码nm_modname = “entry”,会导致 ArkTS 导入时找不到模块。
这里的名字一点要保持一致

我这里对原生的cmake文件做了一个简单的修改大家可以直接用我的cmake
cmake_minimum_required(VERSION 3.14)
if(DEFINED CMAKE_TOOLCHAIN_FILE)
include(${CMAKE_TOOLCHAIN_FILE})
endif()
project(entry)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 新增:添加鸿蒙SDK头文件路径
include_directories(
"${CMAKE_TOOLCHAIN_FILE}/../../include"
"${OHOS_SDK_NATIVE}/include"
"${HMOS_SDK_NATIVE}/include"
)
link_directories(
"${CMAKE_TOOLCHAIN_FILE}/../../libs/${CMAKE_OHOS_ARCH_ABI}"
"${OHOS_SDK_NATIVE}/libs/${CMAKE_OHOS_ARCH_ABI}"
)
add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so)这段代码是鸿蒙 6.0(API20)环境下 NAPI 原生模块的 CMake 构建配置文件,负责指导编译器完成原生代码的编译、链接,最终生成可被 ArkTS 调用的动态库。
cmake_minimum_required(VERSION 3.14)要求当前环境的 CMake 版本不低于 3.14(鸿蒙 6.0 推荐的 CMake 版本,确保兼容新特性)。
if(DEFINED CMAKE_TOOLCHAIN_FILE)
include(${CMAKE_TOOLCHAIN_FILE})
endif()CMAKE_TOOLCHAIN_FILE是 DevEco Studio 自动传入的鸿蒙原生工具链文件路径(包含编译规则、编译器路径等); 通过include导入该文件,让 CMake 遵循鸿蒙的编译标准(如适配鸿蒙的系统架构、编译器选项)。
project(entry)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)project(entry):定义项目名称为entry(需与 NAPI 模块名、动态库名一致); set(CMAKE_CXX_STANDARD 17):指定 C++ 编译标准为 C++17(鸿蒙 NAPI 推荐的标准,兼容现代语法); CMAKE_CXX_STANDARD_REQUIRED ON:强制使用指定的 C++ 标准,若编译器不支持则报错。
include_directories(
"${CMAKE_TOOLCHAIN_FILE}/../../include"
"${OHOS_SDK_NATIVE}/include"
"${HMOS_SDK_NATIVE}/include"
)include_directories:告诉编译器头文件的搜索目录; 路径是从鸿蒙 SDK 中提取的原生头文件目录(如napi/native_api.h、ohos/log.h都存放在这些目录下),避免出现 “头文件找不到” 的编译错误。
link_directories(
"${CMAKE_TOOLCHAIN_FILE}/../../libs/${CMAKE_OHOS_ARCH_ABI}"
"${OHOS_SDK_NATIVE}/libs/${CMAKE_OHOS_ARCH_ABI}"
)link_directories:告诉链接器依赖库的搜索目录; ${CMAKE_OHOS_ARCH_ABI}是自动变量(如arm64-v8a、x86_64),确保链接对应架构的库文件(如 NAPI 核心库libace_napi.z.so)。
add_library(entry SHARED napi_init.cpp)add_library(entry SHARED …):编译生成名为entry的动态库(SHARED表示动态库,鸿蒙 NAPI 必须用动态库); napi_init.cpp是原生代码文件(包含 NAPI 函数、模块注册逻辑),可添加多个源文件(如napi_init.cpp utils.cpp)。
target_link_libraries(entry PUBLIC libace_napi.z.so)target_link_libraries:将当前动态库与鸿蒙 NAPI 核心库libace_napi.z.so链接; libace_napi.z.so是鸿蒙 6.0 的 NAPI 依赖库(旧版本是libnapi.so),提供napi_get_value_double等 NAPI 接口的实现,是 NAPI 开发的必备依赖。
这段 CMake 配置的最终目标是:将napi_init.cpp编译为libentry.so动态库,并确保其能正确调用鸿蒙 NAPI 的接口,最终被 ArkTS 代码导入并调用。
动态库会被编译在这里

我这里在日志部分卡了好久,为了测试是否调用成功来输出日志进行检测。
// 正确导入hilog
import hilog from '@ohos.hilog';
// 导入原生模块
import testNapi from 'libentry.so';
// 自定义日志域
const DOMAIN = 0x1234;
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize($r('app.float.page_text_font_size'))
.fontWeight(FontWeight.Bold)
.onClick(() => {
hilog.info(DOMAIN, 'testTag', 'NAPI调用结果:%{public}d', testNapi.add(2,3));
this.message = 'Welcome';
})
}
.width('100%')
}
.height('100%')
}
}这段代码是鸿蒙 ArkTS 页面组件,核心功能是通过 NAPI 调用 C/C++ 原生方法,并结合日志打印交互结果,是典型的 “ArkTS + NAPI” 跨语言协作示例。
这里其实主要是通过 NAPI 调用 C/C++ 原生方法,对于有基础的朋友们肯定是可以看懂的,我这里为了大多数人还是简单解释下吧
// 正确导入hilog:鸿蒙系统日志工具,用于打印调试/业务日志
import hilog from '@ohos.hilog';
// 导入原生模块:加载C/C++编译生成的动态库libentry.so,testNapi对应原生模块的方法集合
import testNapi from 'libentry.so';@ohos.hilog是鸿蒙官方日志工具,替代了旧版本的@kit.HilogKit; libentry.so是之前通过 CMake 编译生成的 NAPI 动态库,testNapi是该库暴露给 ArkTS 的方法对象(如add方法)。
// 自定义日志域:用于区分不同模块的日志(避免与系统日志冲突)
const DOMAIN = 0x1234;DOMAIN是日志的 “分类标识”(通常用十六进制数),可自定义(需避免使用系统保留的0x0000); 打印日志时需指定该域,方便在 Logcat 中筛选特定模块的日志。
@Entry // 标识该组件是应用的入口页面
@Component // 标识这是一个ArkTS自定义组件
struct Index {
@State message: string = 'Hello World'; // 响应式状态:值变化时会自动更新UI@Entry+@Component是鸿蒙页面组件的基础注解; @State是响应式状态装饰器:当message的值改变时,依赖它的 UI(如Text组件)会自动重新渲染。
build() { // 组件的UI构建方法,定义页面结构
Row() { // 横向容器(类似HTML的div)
Column() { // 纵向容器
Text(this.message) // 文本组件,显示message的内容
.fontSize($r('app.float.page_text_font_size')) // 字体大小(从应用资源文件中读取,支持多端适配)
.fontWeight(FontWeight.Bold) // 字体加粗
.onClick(() => { // 点击事件回调:用户点击Text时执行
// 调用原生模块的add方法,并打印结果(%{public}d是整数占位符)
hilog.info(DOMAIN, 'testTag', 'NAPI调用结果:%{public}d', testNapi.add(2,3));
this.message = 'Welcome'; // 修改响应式状态,触发UI更新
})
}
.width('100%') // 纵向容器占满父容器宽度
}
.height('100%') // 横向容器占满页面高度
}布局采用Row+Column的嵌套结构,是鸿蒙 UI 的基础布局方式; onClick是核心交互逻辑:
这段代码实现了 “ArkTS 触发点击事件 → 调用 C/C++ 原生方法 → 打印结果并更新 UI” 的完整流程,是鸿蒙 NAPI 开发的典型场景:
日志是调试 NAPI 的关键,但容易因 “格式错误”“筛选配置” 导致无输出:

之前尝试QT的时候模拟器打不开好像就是这个问题,需要在配置文件中进行修改,只需要在这里添加一行即可

ABI(Application Binary Interface)是 “应用二进制接口”,决定了原生代码(C/C++)编译后的二进制文件能在哪些 CPU 架构的设备上运行。常见的鸿蒙设备架构有:
适配目标设备:鸿蒙应用需要运行在不同架构的设备上(如真机是arm64-v8a,模拟器是x86_64),指定abiFilters后,CMake 会为这些架构分别编译对应的动态库(libentry.so),保证应用在对应设备上能加载原生模块。
添加"abiFilters": [“arm64-v8a”, “x86_64”]后:

NAPI 并非替代 ArkTS,而是作为其能力补充 —— 它让鸿蒙应用既能享受 ArkTS 的高效开发,又能借助 C/C++ 的性能优势覆盖复杂场景。从概念到实践,NAPI 的核心是 “标准化”:统一的接口、统一的命名、统一的适配规则,让跨语言开发不再是 “黑盒”。 对于鸿蒙开发者而言,掌握 NAPI 不仅是学会一项技术,更是打开了高性能、全场景应用开发的大门 —— 而避开 “名称不匹配”“接口升级” 等坑,正是从 “初识” 到 “熟练” 的关键一步。