查看: 133|回复: 3

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

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
在跨平台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:
  1. target_link_libraries(entry PUBLIC
  2.     libohpreferences.so
  3. )
复制代码
包含头文件:
  1. #include <database/preferences/oh_preferences.h>
  2. #include <database/preferences/oh_preferences_err_code.h>
  3. #include <database/preferences/oh_preferences_option.h>
  4. #include <database/preferences/oh_preferences_value.h>
复制代码

打开和关闭Preferences实例
使用OH_PreferencesOption配置文件名和存储模式后,调用OH_Preferences_Open()。注意:Option对象在Open后可以立即销毁。
  1. OH_PreferencesOption *option = OH_PreferencesOption_Create();
  2. if (option == NULL) return;
  3. int ret = OH_PreferencesOption_SetFileName(option, "my_store");
  4. if (ret != PREFERENCES_OK) { OH_PreferencesOption_Destroy(option); return; }
  5. ret = OH_PreferencesOption_SetStorageType(option, PREFERENCES_STORAGE_XML);
  6. if (ret != PREFERENCES_OK) { OH_PreferencesOption_Destroy(option); return; }
  7. int errCode = PREFERENCES_OK;
  8. OH_Preferences *pref = OH_Preferences_Open(option, &errCode);
  9. OH_PreferencesOption_Destroy(option);
  10. if (pref == NULL || errCode != PREFERENCES_OK) return;
  11. // 使用...
  12. OH_Preferences_Close(pref);
复制代码

写入数据
支持SetInt/SetBool/SetString/SetValue(数组等)。注意:写入后必须调用Flush才能持久化。
  1. OH_Preferences_SetInt(pref, "font_size", 16);
  2. OH_Preferences_SetBool(pref, "dark_mode", true);
  3. OH_Preferences_SetString(pref, "user_name", "张三");
  4. const bool boolFlags[] = {true, false, true, true};
  5. OH_PreferencesValue *val = OH_PreferencesValue_Create();
  6. OH_PreferencesValue_SetBoolArray(val, boolFlags, 4);
  7. OH_Preferences_SetValue(pref, "flags", val);
  8. OH_PreferencesValue_Destroy(val);
  9. OH_Preferences_Flush(pref);
复制代码

读取数据
整型和布尔型直接传指针,字符串需注意释放。
  1. int font_size = 0;
  2. int ret = OH_Preferences_GetInt(pref, "font_size", &font_size);
  3. char *name = NULL;
  4. uint32_t name_len = 0;
  5. ret = OH_Preferences_GetString(pref, "user_name", &name, &name_len);
  6. if (ret == PREFERENCES_OK && name != NULL) {
  7.     // 使用 name
  8.     OH_Preferences_FreeString(name);
  9.     name = NULL;
  10. }
复制代码

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

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

数据变更订阅
注册后,回调在Close()时批量触发,不是实时。
  1. void DataChangeCallback(void *context, const OH_PreferencesPair *pairs, uint32_t count) {
  2.     for (uint32_t i = 0; i < count; i++) {
  3.         const char *key = OH_PreferencesPair_GetKey(pairs, i);
  4.         // 处理变更
  5.     }
  6. }
  7. const char *keys[] = {"font_size", "dark_mode"};
  8. OH_Preferences_RegisterDataObserver(pref, NULL, DataChangeCallback, keys, 2);
  9. // ...操作...
  10. 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,开发效率更高。
回复

使用道具 举报

发表于 3 小时前 | 显示全部楼层

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

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

使用道具 举报

发表于 3 小时前 | 显示全部楼层

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

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

使用道具 举报

发表于 3 小时前 | 显示全部楼层

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

感谢楼主的详细分享!这篇文章对跨平台C++集成很有帮助,尤其是模式兼容和Flush的坑,之前没注意回调时机的问题差点踩了。请问在大量并发写入时,手动Flush的频率是否有推荐值,或者有没有类似事务的批量写入方式?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-22 19:11 , Processed in 0.030531 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部