在跨平台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,开发效率更高。 |