将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)
- 在鸿蒙应用工程的
entry
模块下创建目录:libs/arm64-v8a
。 将编译好的
libcjson.so
文件复制到libs/arm64-v8a
目录中。- 提示: 为了方便在 Windows 模拟器上调试,你可以额外编译一份
x86_64
架构的libcjson.so
并放入libs/x86_64
目录。
- 提示: 为了方便在 Windows 模拟器上调试,你可以额外编译一份
3.2 导入头文件 (.h)
- 在
entry/src/main/cpp/
下创建目录结构:thirdparty/cJSON/arm64-v8a/include
。 - 将 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
- 打开
entry/src/main/cpp/napi_init.cpp
文件。 包含头文件:
#include "thirdparty/cJSON/arm64-v8a/include/cJSON.h" // 使用项目内包含路径
添加一个 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
释放内存。修改
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 中声明和使用
打开
entry/src/main/cpp/types/libentry/Index.d.ts
文件,添加新函数的类型声明:export const add: (a: number, b: number) => number; export const cJSON_CreateObject: () => any; // 声明新函数
在 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')) } ... } }
- 运行调试应用,点击 "Test cJSON" 按钮,即可在 Log (HiLog) 中看到输出信息。
3.6 关于直接使用预编译 so 的说明
前述步骤是在一个 Native C++ 鸿蒙工程中集成并封装了预编译的 libcjson.so
。如果你想在一个非 Native C++ 的纯 ArkTS 鸿蒙工程中直接调用 libcjson.so
提供的函数,则需要:
- 修改 cJSON 源码,添加符合鸿蒙 NAPI 规范的入口函数。
- 使用 DevEco Studio 创建一个新的 Native C++ 工程。
- 将修改后的 cJSON 源码加入该工程。
- 编译生成新的、包含必要 NAPI 接口的
libentry.so
(或自定义名称)。 - 在纯 ArkTS 工程中导入并调用这个新编译的
libentry.so
。
具体操作可参考:编译一份适用于鸿蒙ArkTs的so动态库教学,提供给第三方导入并使用
4. 总结
本次成功将 C++ 的 cJSON 库交叉编译为鸿蒙设备(ARM64)可用的动态库(libcjson.so
),并集成到一个鸿蒙 Native C++ 工程中,通过 NAPI 将其功能暴露给 ArkTS 层使用。这个过程验证了鸿蒙对 C/C++ 生态的支持能力,为后续移植更复杂的 C++ 项目到鸿蒙平台打下了基础。关键点在于正确配置鸿蒙的交叉编译工具链、管理库文件和头文件的路径,以及遵循 NAPI 规范进行封装。