将C++项目(cJSON库)移植到鸿蒙设备的记录

1. 环境准备

1.1 安装 DevEco Studio

下载并安装最新版本的 DevEco Studio,这是鸿蒙应用开发的官方IDE。

1.2 下载 cJson 源码

从官方仓库获取 cJSON 源代码:

git clone https://github.com/DaveGamble/cJSON.git
cd cJSON

1.3 配置环境变量

将鸿蒙 SDK 中的 CMake 路径添加到系统的 PATH 环境变量中,确保命令行可以识别 cmake 命令:

# 示例路径 (根据你的实际安装位置调整):
C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\native\build-tools\cmake\bin

2. 编译 cJSON 库

2.1 准备编译目录

在 cJSON 源码根目录下创建构建输出文件夹并进入:

mkdir build
cd build

2.2 配置 CMake 并进行交叉编译

本步骤主要参考了以下资源:

使用 CMake 生成 Ninja 构建文件,并指定鸿蒙的工具链、编译器和目标架构:

cmake -G Ninja ^
  -DCMAKE_TOOLCHAIN_FILE="C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\native\build\cmake\ohos.toolchain.cmake" ^
  -DCMAKE_C_COMPILER="C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\native\llvm\bin\clang.exe" ^
  -DCMAKE_CXX_COMPILER="C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\native\llvm\bin\clang++.exe" ^
  -DCMAKE_MAKE_PROGRAM="C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\native\build-tools\cmake\bin\ninja.exe" ^
  -DOHOS_ARCH=arm64-v8a ..

命令参数详解:

  • -G "Ninja": 指定生成器为 Ninja(鸿蒙推荐使用的构建系统)。
  • -DCMAKE_TOOLCHAIN_FILE=...: 指定鸿蒙的交叉编译工具链配置文件。
  • -DCMAKE_C_COMPILER=...: 指定用于 C 的编译器(鸿蒙 SDK 中的 Clang)。
  • -DCMAKE_CXX_COMPILER=...: 指定用于 C++ 的编译器(鸿蒙 SDK 中的 Clang++)。
  • -DCMAKE_MAKE_PROGRAM=...: 指定 Ninja 可执行文件的路径。
  • -DOHOS_ARCH=arm64-v8a: 指定目标架构为 ARM64(根据你的设备选择,如 armeabi-v7a)。

配置和生成过程耗时因电脑性能而异(示例输出耗时约 55 秒):

-- Configuring done (55.4s)
-- Generating done (0.1s)
-- Build files have been written to: C:/ws/cJSON/build

2.3 执行编译

使用 CMake 触发 Ninja 进行实际编译,生成 Release 版本的库文件:

cmake --build . --config Release

编译完成后,在 build 目录下即可找到生成的 libcjson.so 动态库文件。

3. 在鸿蒙 Native C++ 项目中使用 cJSON

3.1 导入库文件 (.so)

  1. 在鸿蒙应用工程的 entry 模块下创建目录:libs/arm64-v8a
  2. 将编译好的 libcjson.so 文件复制到 libs/arm64-v8a 目录中。

    • 提示: 为了方便在 Windows 模拟器上调试,你可以额外编译一份 x86_64 架构的 libcjson.so 并放入 libs/x86_64 目录。

3.2 导入头文件 (.h)

  1. entry/src/main/cpp/ 下创建目录结构:thirdparty/cJSON/arm64-v8a/include
  2. 将 cJSON 源码中的 cJSON.h 头文件复制到 thirdparty/cJSON/arm64-v8a/include 目录中。

3.3 修改 CMakeLists.txt

entry/src/main/cpp/CMakeLists.txt 文件中,添加链接库和包含目录的指令:

# 将三方库 libcjson.so 链接到主目标 (entry)
target_link_libraries(entry PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${OHOS_ARCH}/libcjson.so
)

# 将三方库 cJSON 的头文件目录包含进来
target_include_directories(entry PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cJSON/${OHOS_ARCH}/include
)

3.4 在 C++ 代码中使用 cJSON

完成上述配置后,即可在工程的 C++ 代码中引入和使用 cJSON 库。

示例:封装 cJSON_CreateObject

  1. 打开 entry/src/main/cpp/napi_init.cpp 文件。
  2. 包含头文件:

    #include "thirdparty/cJSON/arm64-v8a/include/cJSON.h" // 使用项目内包含路径
  3. 添加一个 NAPI 函数封装 cJSON_CreateObject

    napi_value Fn_cJSON_CreateObject(napi_env env, napi_callback_info info) {
        // 创建 cJSON 对象
        cJSON *cObject = cJSON_CreateObject();
    
        // 创建一个 napi_value 对象用于返回
        napi_value result;
        napi_create_object(env, &result);
    
        // 示例:设置 cObject 的 valuestring (实际应用中根据需求填充对象)
        char *returnStr = "你很棒呀!加油打工人";
        cJSON_AddStringToObject(cObject, "message", returnStr); // 更规范的添加方式
    
        // 将 cJSON 对象的部分属性转换为 NAPI 值并添加到返回对象中 (示例性质)
        napi_value type;
        napi_create_int32(env, cObject->type, &type);
        napi_set_named_property(env, result, "type", type);
    
        // ... (类似地转换和添加其他属性如 valueint, valuestring, string, valuedouble, next, prev, child)
        // 注意:示例中直接访问 cObject->valuestring 可能不安全,应使用 cJSON_GetObjectItemCaseSensitive 等函数获取
    
        // 释放创建的 cJSON 对象 (重要!避免内存泄漏)
        cJSON_Delete(cObject);
    
        return result;
    }

    注意: 上面的示例为了演示集成过程,直接操作了 cObject 的内部结构(如 cObject->valuestring)。在实际应用中,强烈建议使用 cJSON 提供的 API(如 cJSON_AddStringToObject, cJSON_GetObjectItemCaseSensitive, cJSON_Print 等)来安全地创建、访问和修改 JSON 对象。直接操作内部指针容易出错且不安全。示例末尾添加了 cJSON_Delete 释放内存。

  4. 修改 Init 函数,将新封装的函数暴露给 ArkTS:

    static napi_value Init(napi_env env, napi_value exports) {
        napi_property_descriptor desc[] = {
            { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
            { "cJSON_CreateObject", nullptr, Fn_cJSON_CreateObject, nullptr, nullptr, nullptr, napi_default, nullptr } // 暴露新函数
        };
        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
        return exports;
    }

3.5 在 ArkTS 中声明和使用

  1. 打开 entry/src/main/cpp/types/libentry/Index.d.ts 文件,添加新函数的类型声明:

    export const add: (a: number, b: number) => number;
    export const cJSON_CreateObject: () => any; // 声明新函数
  2. 在 UI 页面(如 entry/src/main/ets/pages/Index.ets)中添加按钮调用该函数:

    import testNapi from 'libentry.so'; // 导入 native 模块
    
    interface cJSON {
      next: cJSON
      prev: cJOSN
      child: cJSON
      type: number
      valuestring: string
      valueint: number
      valuedouble: number
      string: string
    }
    
    @Entry
    @Component
    struct Index {
      ...
      build() {
        Column() {
          ...
          Button('Test cJSON')
            .onClick(() => {
              // 调用 native 函数
              let result = testNapi.cJSON_CreateObject();
              // 打印结果 (示例中访问了内部字段,实际应通过API获取JSON字符串)
              hilog.info(0x0000, 'testTag', 'cJSON Test result message: ' + result.message);
            })
            .height(100)
            .width(300)
            .fontSize($r('app.float.page_text_font_size'))
        }
        ...
      }
    }
  3. 运行调试应用,点击 "Test cJSON" 按钮,即可在 Log (HiLog) 中看到输出信息。

3.6 关于直接使用预编译 so 的说明

前述步骤是在一个 Native C++ 鸿蒙工程中集成并封装了预编译的 libcjson.so。如果你想在一个非 Native C++ 的纯 ArkTS 鸿蒙工程中直接调用 libcjson.so 提供的函数,则需要:

  1. 修改 cJSON 源码,添加符合鸿蒙 NAPI 规范的入口函数。
  2. 使用 DevEco Studio 创建一个新的 Native C++ 工程。
  3. 将修改后的 cJSON 源码加入该工程。
  4. 编译生成新的、包含必要 NAPI 接口的 libentry.so (或自定义名称)。
  5. 在纯 ArkTS 工程中导入并调用这个新编译的 libentry.so

具体操作可参考:编译一份适用于鸿蒙ArkTs的so动态库教学,提供给第三方导入并使用

4. 总结

本次成功将 C++ 的 cJSON 库交叉编译为鸿蒙设备(ARM64)可用的动态库(libcjson.so),并集成到一个鸿蒙 Native C++ 工程中,通过 NAPI 将其功能暴露给 ArkTS 层使用。这个过程验证了鸿蒙对 C/C++ 生态的支持能力,为后续移植更复杂的 C++ 项目到鸿蒙平台打下了基础。关键点在于正确配置鸿蒙的交叉编译工具链、管理库文件和头文件的路径,以及遵循 NAPI 规范进行封装。

最后修改:2025 年 08 月 08 日
如果觉得我的文章对你有用,请随意赞赏