OpenHarmony 与 HarmonyOS 的 NAPI 开发实战对比:自上而下与自下而上的差异解析

一、 NAPI框架概述

NAPI(Native API)是鸿蒙生态中用于实现ArkTS/JS与C/C++代码互操作的核心框架。其概念源自Node.js,为了实现JavaScript脚本与C++库之间的相互调用,Node.js对V8引擎的API做了一层封装,称为NAPI。

OpenHarmony系统沿用了NAPI的接口定义形式,但每个接口的内部实现都进行了重写。这是因为NAPI接口的本质是帮助C++程序去跟JavaScript引擎交互,对于不同的引擎需要有不同的实现方式。当用户调用了NAPI接口(如 napi_create_int64()),在Node.js中它会访问V8引擎的API,而在OpenHarmony中,它访问的是ArkUI框架自己的JS引擎。

NAPI主要解决以下问题:

  1. 性能需求:对于CPU密集型、IO密集型或需要直接操作硬件的场景,C/C++实现通常比纯ArkTS/JS更高效。
  2. 代码复用:允许将现有的C/C++库(如音视频编解码、加密算法等)快速移植到鸿蒙平台。
  3. 系统级访问:提供对操作系统底层功能的访问能力,如GPIO控制、特定硬件驱动交互等。

二、 OpenHarmony与HarmonyOS NAPI开发的核心区别

虽然两者都使用NAPI框架,但其目标、开发流程和部署方式有本质区别。

特性 OpenHarmony (系统级扩展) HarmonyOS (应用级模块)
目标 扩展系统能力,为所有应用提供新的原生API。 增强单个应用,封装应用私有的高性能逻辑。
开发环境 需要完整的OpenHarmony源码和Linux编译环境。 使用DevEco Studio,无需系统源码。
SDK依赖 依赖并需要修改Full-SDK,将新接口打包进系统镜像。 使用Public-SDK,模块随应用打包。
构建系统 使用GNohos.build配置,参与系统编译。 使用CMake配置,由DevEco Studio编译。
模块位置 编译后.so文件位于/system/lib/module/目录,成为系统一部分。 编译后.so文件打包在HAP内,位于应用私有目录。
ArkTS导入 import myApi from '@ohos.myNewApi' (标准系统接口名)。 import nativeModule from 'libentry.so' (直接指定so名)。
适用场景 OEM厂商、系统开发者,为特定硬件或平台能力提供标准接口。 应用开发者,优化应用内算法、复用现有C/C++库。

OpenHarmony的NAPI开发是一种"自下而上"的模式,开发者修改系统源码,增加一个新的子系统/组件,编译后生成包含新原生接口的系统固件和Full-SDK。应用开发者拿到这个新的SDK后,就能像使用系统官方API一样使用新增的接口。而HarmonyOS的NAPI开发是一种"自上而下"的模式,开发者在应用工程中直接创建一个原生模块,这个模块只为当前应用服务,与系统其他部分隔离。

三、 OpenHarmony NAPI开发:新增系统API

以下步骤演示如何在OpenHarmony源码中新增一个名为hellonapi的系统API模块,提供一个简单的add函数。

(一) 环境准备

  1. OpenHarmony源码。
  2. Linux编译环境(如Ubuntu 18.04/20.04)。
  3. 预装编译工具链。

(二) 新增子系统与组件

  1. 创建子系统目录 :在源码根目录创建 mysubsys 文件夹。

  2. 配置子系统 :在 mysubsys 下创建 ohos.build 文件,定义子系统和组件。

    json 复制代码
    // mysubsys/ohos.build
    {
      "subsystem": "mysubsys",
      "parts": {
        "hello": {
          "module_list": [
            "//mysubsys/hello/hellonapi:hellonapi"
          ]
        }
      }
    }
  3. 注册子系统 :修改 build/subsystem_config.json,将新子系统加入构建系统。

    json 复制代码
    // build/subsystem_config.json (片段)
    "mysubsys": {
      "path": "mysubsys",
      "name": "mysubsys"
    },

(三) 实现NAPI模块

  1. 创建目录结构

    复制代码
    mysubsys/
    └── hello/
        └── hellonapi/
            ├── hellonapi.cpp
            └── BUILD.gn
  2. 编写C++代码 (hellonapi.cpp)

    cpp 复制代码
    #include "napi/native_api.h"
    #include "napi/native_node_api.h"
    // C++业务实现函数
    static napi_value Add(napi_env env, napi_callback_info info) {
        size_t argc = 2;
        napi_value args[2];
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
        if (argc < 2) {
            napi_throw_error(env, nullptr, "Invalid argument count. Expected 2.");
            return nullptr;
        }
        napi_valuetype valuetype0;
        napi_typeof(env, args[0], &valuetype0);
        napi_valuetype valuetype1;
        napi_typeof(env, args[1], &valuetype1);
        if (valuetype0 != napi_number || valuetype1 != napi_number) {
            napi_throw_type_error(env, nullptr, "Wrong argument type. Numbers expected.");
            return nullptr;
        }
        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;
    }
    // 接口注册函数
    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;
    }
    // 模块定义
    static napi_module hellonapiModule = {
        .nm_version = 1,
        .nm_flags = 0,
        .nm_filename = nullptr,
        .nm_register_func = Init,
        .nm_modname = "hellonapi", // 模块名,对应ArkTS导入名的一部分
        .nm_priv = ((void*)0),
        .reserved = {0},
    };
    // 模块注册,加载so时自动调用
    extern "C" __attribute__((constructor)) void RegisterHellonapiModule(void) {
        napi_module_register(&hellonapiModule);
    }
  3. 编写构建脚本 (BUILD.gn)

    gn 复制代码
    import("//build/ohos.gni")
    ohos_shared_library("hellonapi") {
        include_dirs = [
            "//foundation/ace/napi/interfaces/kits"
        ]
        sources = [
            "hellonapi.cpp"
        ]
        deps = [
            "//foundation/ace/napi:ace_napi",
        ]
        relative_install_dir = "module" // 关键:指定安装到/system/lib/module/
        subsystem_name = "mysubsys"
        part_name = "hello"
    }

(四) 编译与集成

  1. 编译系统 :执行 ./build.sh --product-name <你的产品名> (如 rk3568)。

  2. 生成Full-SDK :编译成功后,新的 libhellonapi.z.so 会被打包到系统镜像中,并生成对应的接口声明文件(.d.ts)到 out/<product>/packages/phone/samples/full-sdk 目录。

  3. ArkTS应用调用 :应用开发者将这个新的Full-SDK集成到DevEco Studio中,就可以像使用系统API一样调用它:

    typescript 复制代码
    import hellonapi from '@ohos.hellonapi'; // 注意:模块名通常是 @ohos. + nm_modname
    @Entry
    @Component
    struct Index {
      @State result: number = 0;
      build() {
        Row() {
          Column() {
            Text(`Result: ${this.result}`)
            Button('Add 5 + 3')
              .onClick(() => {
                this.result = hellonapi.add(5, 3);
              })
          }
        }
      }
    }

四、 HarmonyOS NAPI开发:创建应用原生模块

以下步骤演示如何在HarmonyOS应用工程中创建一个原生模块。

(一) 环境准备

  1. DevEco Studio。
  2. HarmonyOS SDK(Public-SDK即可)。

(二) 创建Native工程

  1. 在DevEco Studio中,选择 File > New > Create Project
  2. 选择模板 Native C++,点击Next。
  3. 配置工程名、包名等,点击Finish。
  4. DevEco Studio会自动生成一个包含 napi-init.cppCMakeLists.txt 和示例ArkTS代码的工程。

(三) 实现NAPI模块

  1. 编写C++代码 :在 entry/src/main/cpp 目录下,修改或创建 .cpp 文件(例如 hello.cpp)。

    cpp 复制代码
    // entry/src/main/cpp/hello.cpp
    #include "napi/native_api.h"
    // 与OpenHarmony示例中的Add函数完全相同
    static napi_value Add(napi_env env, napi_callback_info info) {
        // ... (代码同上) ...
    }
    // 此函数由napi-init.cpp中的Init函数调用
    napi_value CreateAddFunction(napi_env env) {
        napi_property_descriptor desc[] = {
            {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}
        };
        napi_value result;
        napi_create_object(env, &result);
        napi_define_properties(env, result, sizeof(desc) / sizeof(desc[0]), desc);
        return result;
    }
  2. 注册接口 :打开 entry/src/main/cpp/napi-init.cpp,修改 Init 函数来导出你的接口。

    cpp 复制代码
    // entry/src/main/cpp/napi-init.cpp
    #include "napi/native_api.h"
    // 声明你的函数创建器
    extern napi_value CreateAddFunction(napi_env env);
    static napi_value Init(napi_env env, napi_value exports) {
        // 将你的函数对象挂载到exports上
        napi_set_named_property(env, exports, "addModule", CreateAddFunction(env));
        return exports;
    }
    // ... (其余自动生成的代码保持不变) ...
  3. 配置构建脚本 (CMakeLists.txt)

    cmake 复制代码
    # entry/src/main/cpp/CMakeLists.txt
    cmake_minimum_required(VERSION 3.4.1)
    project(Entry)
    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    if(DEFINED PACKAGE_FIND_FILE)
        include(${PACKAGE_FIND_FILE})
    endif()
    include_directories(${NATIVERENDER_ROOT_PATH}
                        ${NATIVERENDER_ROOT_PATH}/include)
    add_library(entry SHARED hello.cpp napi-init.cpp) # 添加你的cpp文件
    find_package(hilog REQUIRED CONFIG)
    find_package(ace_napi REQUIRED CONFIG)
    target_link_libraries(entry PUBLIC ace_napi::ace_napi hilog::libhilog)

(四) ArkTS应用调用

  1. 编译项目 :DevEco Studio会自动编译C++代码生成 libentry.so 并打包到HAP中。

  2. 在ArkTS中调用

    typescript 复制代码
    // entry/src/main/ets/pages/Index.ets
    import nativeModule from 'libentry.so'; // 直接导入so文件名
    @Entry
    @Component
    struct Index {
      @State result: number = 0;
      build() {
        Row() {
          Column() {
            Text(`Result: ${this.result}`)
            Button('Add 10 + 20')
              .onClick(() => {
                // 通过在Init中挂载的对象名访问
                this.result = nativeModule.addModule.add(10, 20);
              })
          }
        }
      }
    }

五、 NAPI异步开发:Callback与Promise

对于耗时操作,必须使用异步模型避免阻塞UI线程。NAPI提供了基于napi_create_async_work的异步支持。

异步工作原理

  1. 立即返回:原生函数被调用时,立即创建一个异步工作项并返回一个Promise对象或接收一个Callback函数,然后立即结束。
  2. 后台执行napi_create_async_work创建的工作项被放入线程池,在后台Worker线程中执行execute回调。注意:execute回调中不能调用任何NAPI接口
  3. 结果返回execute执行完毕后,在JS主线程(EventLoop)中触发complete回调。complete回调可以将结果转换为JS值,并通过Promise的resolve/reject或调用Callback函数返回给ArkTS。

Promise模式示例

cpp 复制代码
// 结构体用于在execute和complete之间传递数据
struct AddAsyncData {
    napi_async_work work;
    double x;
    double y;
    double result;
    napi_ref callback_ref; // 如果是Callback模式,需要保存回调函数的引用
};
// execute回调:在worker线程执行
static void ExecuteAdd(napi_env env, void* data) {
    AddAsyncData* asyncData = (AddAsyncData*)data;
    asyncData->result = asyncData->x + asyncData->y;
}
// complete回调:在JS线程执行
static void CompleteAdd(napi_env env, napi_status status, void* data) {
    AddAsyncData* asyncData = (AddAsyncData*)data;
    napi_value result;
    napi_create_double(env, asyncData->result, &result);
    // 获取Promise的resolve函数
    napi_value promise;
    napi_get_named_property(env, asyncData->work, "promise", &promise);
    napi_value resolve;
    napi_get_named_property(env, promise, "resolve", &resolve);
    // 调用resolve,将结果返回
    napi_call_function(env, undefined, resolve, 1, &result, nullptr);
    // 清理资源
    napi_delete_async_work(env, asyncData->work);
    delete asyncData;
}
// 导出的异步函数
static napi_value AddAsync(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // 获取参数
    AddAsyncData* asyncData = new AddAsyncData();
    napi_get_value_double(env, args[0], &asyncData->x);
    napi_get_value_double(env, args[1], &asyncData->y);
    // 创建Promise
    napi_value promise;
    napi_create_promise(env, &asyncData->callback_ref, &promise, &resolve); // 此处简化,实际需管理deferred
    // 创建异步工作项
    napi_value resource_name;
    napi_create_string_utf8(env, "AddAsync", NAPI_AUTO_LENGTH, &resource_name);
    napi_create_async_work(env, nullptr, resource_name, ExecuteAdd, CompleteAdd, asyncData, &asyncData->work);
    
    // 将工作项放入队列
    napi_queue_async_work(env, asyncData->work);
    return promise; // 返回Promise对象
}

Callback模式的实现类似 ,只是在AddAsync中获取并保存Callback函数的引用,在CompleteAdd中调用该Callback即可。

六、 总结

NAPI是鸿蒙生态连接ArkTS应用层与C/C++系统/原生层的强大桥梁。通过本教程,我们清晰地认识到:

  1. OpenHarmony的NAPI开发是系统级工程,旨在为整个生态扩展标准化的原生能力,流程复杂但影响深远,适合平台和硬件开发者。
  2. HarmonyOS的NAPI开发是应用级工程,旨在优化单个应用的性能或复用现有代码库,流程简单直接,适合广大应用开发者。
相关推荐
喵手5 小时前
【参赛心得】从“碰一碰”到“服务流转”:HarmonyOS创新赛金奖作品“智游文博”全流程复盘!
华为·harmonyos·鸿蒙应用开发·1024征文
鸿蒙小白龙5 小时前
OpenHarmony平台大语言模型本地推理:llama深度适配与部署技术详解
人工智能·语言模型·harmonyos·鸿蒙·鸿蒙系统·llama·open harmony
安卓开发者5 小时前
鸿蒙NEXT Wear Engine开发实战:手机侧应用如何调用穿戴设备能力
华为·智能手机·harmonyos
Damon小智5 小时前
仓颉 Markdown 解析库在 HarmonyOS 应用中的实践
华为·typescript·harmonyos·markdown·三方库
ZIM学编程7 小时前
把握鸿蒙生态红利:HarmonyOS 应用开发学习路径与实战课程推荐
学习·华为·harmonyos
安卓开发者1 天前
鸿蒙NEXT应用接入快捷栏:一键直达,提升用户体验
java·harmonyos·ux
HMS Core1 天前
消息推送策略:如何在营销与用户体验间找到最佳平衡点
华为·harmonyos·ux
Brianna Home1 天前
【案例实战】鸿蒙分布式调度:跨设备协同实战
华为·wpf·harmonyos
Bert丶seven1 天前
鸿蒙Harmony实战开发教学(No.4)-RichText组件基础到高阶介绍篇
华为·harmonyos·arkts·鸿蒙·鸿蒙系统·arkui·开发教程