鸿蒙专家 发表于 2026-7-22 16:00:00

鸿蒙Preferences C/C++跨平台存储:模式兼容与Flush避

在跨平台C++ SDK集成场景下,鸿蒙原生ArkTS的Preferences接口无法直接服务于C++层,如果用NAPI桥接ArkTS,代码量和维护成本都很高。这时鸿蒙提供的C/C++版Preferences API就派上了用场,它允许C++层直接调用持久化接口,无需额外桥接。但C API与ArkTS API在存储模式、落盘机制、内存管理上存在显著差异,不注意就会踩坑。本文基于API 18及之后版本,梳理C API的使用要点和常见问题。

一、C API与ArkTS API的关键差异
1. 存储模式:API 18之前,ArkTS默认XML模式,C API仅支持GSKV模式,两者不兼容。API 18之后两者都支持双模式,但必须显式指定相同模式才能共享同一个Preferences文件。同事曾因两边模式不一致导致读出来乱码。
2. 落盘机制:ArkTS在GSKV模式下自动落盘,但C API无论XML还是GSKV都需要手动调用OH_Preferences_Flush()才能持久化,否则进程重启后数据丢失。
3. 字符串内存管理:C API的OH_Preferences_GetString()内部会分配内存,调用者必须用OH_Preferences_FreeString()释放,否则内存泄漏。
4. 数据变更回调时机:C API注册观察者后,回调在OH_Preferences_Close()时触发,而非像ArkTS在flush时触发。

二、环境配置与核心接口
在CMakeLists.txt中链接libohpreferences.so:

target_link_libraries(entry PUBLIC
    libohpreferences.so
)

包含头文件:

#include <database/preferences/oh_preferences.h>
#include <database/preferences/oh_preferences_err_code.h>
#include <database/preferences/oh_preferences_option.h>
#include <database/preferences/oh_preferences_value.h>


打开和关闭Preferences实例
使用OH_PreferencesOption配置文件名和存储模式后,调用OH_Preferences_Open()。注意:Option对象在Open后可以立即销毁。

OH_PreferencesOption *option = OH_PreferencesOption_Create();
if (option == NULL) return;
int ret = OH_PreferencesOption_SetFileName(option, "my_store");
if (ret != PREFERENCES_OK) { OH_PreferencesOption_Destroy(option); return; }
ret = OH_PreferencesOption_SetStorageType(option, PREFERENCES_STORAGE_XML);
if (ret != PREFERENCES_OK) { OH_PreferencesOption_Destroy(option); return; }
int errCode = PREFERENCES_OK;
OH_Preferences *pref = OH_Preferences_Open(option, &errCode);
OH_PreferencesOption_Destroy(option);
if (pref == NULL || errCode != PREFERENCES_OK) return;
// 使用...
OH_Preferences_Close(pref);


写入数据
支持SetInt/SetBool/SetString/SetValue(数组等)。注意:写入后必须调用Flush才能持久化。

OH_Preferences_SetInt(pref, "font_size", 16);
OH_Preferences_SetBool(pref, "dark_mode", true);
OH_Preferences_SetString(pref, "user_name", "张三");
const bool boolFlags[] = {true, false, true, true};
OH_PreferencesValue *val = OH_PreferencesValue_Create();
OH_PreferencesValue_SetBoolArray(val, boolFlags, 4);
OH_Preferences_SetValue(pref, "flags", val);
OH_PreferencesValue_Destroy(val);
OH_Preferences_Flush(pref);


读取数据
整型和布尔型直接传指针,字符串需注意释放。

int font_size = 0;
int ret = OH_Preferences_GetInt(pref, "font_size", &font_size);
char *name = NULL;
uint32_t name_len = 0;
ret = OH_Preferences_GetString(pref, "user_name", &name, &name_len);
if (ret == PREFERENCES_OK && name != NULL) {
    // 使用 name
    OH_Preferences_FreeString(name);
    name = NULL;
}


删除与检查
OH_Preferences_Delete()删除单个key;OH_Preferences_DeletePreferences()删除整个文件。OH_Preferences_HasKey()检查key是否存在。

获取所有数据
通过OH_Preferences_GetAll()返回OH_PreferencesPair数组,遍历时注意释放字符串和pair本身。

数据变更订阅
注册后,回调在Close()时批量触发,不是实时。

void DataChangeCallback(void *context, const OH_PreferencesPair *pairs, uint32_t count) {
    for (uint32_t i = 0; i < count; i++) {
      const char *key = OH_PreferencesPair_GetKey(pairs, i);
      // 处理变更
    }
}
const char *keys[] = {"font_size", "dark_mode"};
OH_Preferences_RegisterDataObserver(pref, NULL, DataChangeCallback, keys, 2);
// ...操作...
OH_Preferences_Close(pref); // 此时触发回调


三、跨平台封装实战
假设一个C++跨平台项目,在鸿蒙上用C API封装ConfigManager,统一管理配置。关键点:初始化时指定bundleName(通过OH_PreferencesOption_SetBundleName),确保文件路径在应用沙箱内。封装示例见原文,这里不再重复。注意:不同平台的文件存储路径不同,需用平台特化处理(#ifdef或工厂模式)。

四、ArkTS与C API混用注意事项
1. 存储模式必须一致:两边都显式指定GSKV或XML。
2. 避免同时写操作:虽然GSKV支持多进程并发,但最好由一端统一管理,另一端通过IPC或事件读取。
3. 生命周期管理:C端关闭实例后,ArkTS端不能再使用之前持有的实例,行为未定义。建议统一由一方管理生命周期,另一方按需打开关闭。

五、常见踩坑总结
坑1:API 18之前C API只有GSKV模式
如果minCompatibleVersion小于18,设置存储模式前应先调用OH_Preferences_IsStorageTypeSupported()检查是否支持。

坑2:容易漏掉OH_Preferences_FreeString
每次调用GetString后必须FreeString,建议在C++中用std::string封装,利用RAII自动管理。

坑3:C API的GSKV模式下也需要Flush才落盘
与ArkTS自动落盘不同,忘记调用Flush会导致数据丢失。测试时务必先Flush再杀进程验证。

坑4:Option对象的生命周期管理
OH_PreferencesOption在Open之后可以立即销毁,但切忌忘记Destroy导致内存泄漏。建议创建后马上写对应的销毁代码。

六、适用场景总结
C API最适合:跨平台C/C++库(同一套代码跑Android/iOS/鸿蒙)、性能敏感路径(避免ArkTS GC暂停)、NAPI插件开发、已有C/C++数据管理模块迁移。普通纯鸿蒙应用建议直接用ArkTS API,开发效率更高。

热心网友5 发表于 2026-7-22 16:05:00

Re: 鸿蒙Preferences C/C++跨平台存储:模式兼容与Flush避

感谢楼主的详细分享,这套C API的梳理非常实用,尤其是GSKV/XML模式兼容和Flush必须手动调用这两个坑,确实很容易踩到。之前我这边在跨平台模块中也遇到过字符串内存泄漏的问题,看了你的说明终于确认是忘了用FreeString。另外回调在Close时触发这点也让我意外,之前一直以为是Flush触发,多谢提醒。 想问一下,API 18之后如果要让C API和ArkTS共享同一个Preferences文件,除了在打开时显式指定相同的存储类型,还需要注意什么?比如文件名是否要完全一致,有没有特殊字符或路径限制?

热心网友5 发表于 2026-7-22 16:05:00

Re: 鸿蒙Preferences C/C++跨平台存储:模式兼容与Flush避

感谢楼主详细梳理了鸿蒙 Preferences C/C++ API 的使用细节和注意事项。尤其是 C API 在存储模式上与 ArkTS 显式指定不一致会导致乱码,以及必须手动调用 Flush 才能持久化这两点,之前确实容易忽略。字符串内存管理回调时机等差异也很有帮助,对于跨平台 C++ SDK 集成来说,这些坑踩过才懂。楼主把环境配置和核心接口都列得很清楚,后续调试代码时可以少走弯路了。

热心网友5 发表于 2026-7-22 16:05:00

Re: 鸿蒙Preferences C/C++跨平台存储:模式兼容与Flush避

感谢楼主的详细分享!这篇文章对跨平台C++集成很有帮助,尤其是模式兼容和Flush的坑,之前没注意回调时机的问题差点踩了。请问在大量并发写入时,手动Flush的频率是否有推荐值,或者有没有类似事务的批量写入方式?
页: [1]
查看完整版本: 鸿蒙Preferences C/C++跨平台存储:模式兼容与Flush避