当前位置: 首页 > news >正文

HarmonyOS7 数据持久化:Preferences 和 RDB 到底选哪个?

文章目录

    • 前言
    • 两种方案定位
    • Preferences 实战:轻量 KV 存储
      • 完整代码
      • API 逐个讲解
      • 使用示例
    • RDB 实战:关系型数据库 CRUD
      • 完整代码
      • SQL 和 API 讲解
    • 性能对比数据
    • 选型建议
    • 写在最后

前言

每次做新项目,数据持久化这块我都会纠结——用 Preferences 还是 RDB?选轻量了不够用,选重量了又杀鸡用牛刀。踩了几次坑之后,我总算搞明白了这俩的边界。今天把经验分享一下,帮你少走弯路。
存个用户设置、缓存个登录状态,用 Preferences 三行代码搞定。但要存聊天记录、商品列表这种有关系的复杂数据,Preferences 就撑不住了,得上 RDB。问题在于,很多人不知道什么时候该选哪个,干脆全用 RDB,结果简单配置项也要写 SQL,累不累?

两种方案定位

先看核心区别,一张表搞定:

维度PreferencesRDB (关系型数据库)
底层KV 键值对文件SQLite
数据结构Key-Value表、行、列
适合数据量< 1万条,轻量级无硬限制,百万级都行
查询能力只能按 Key 查支持 SQL 全量查询
事务支持有(begin/commit/rollback)
数据类型number/string/boolean/Array任意 SQL 类型
并发安全不支持多进程支持
学习成本低,5 分钟上手中,要会写 SQL
典型场景用户设置、登录标记、引导状态聊天记录、商品数据、订单信息

|Key 限制| 长度 ≤ 1024 字节 | 无此限制 |
|Value 限制| string ≤ 16MB | 无此限制 |
|缓存机制| 内存缓存,读取极快 | 无内存缓存 |

一句话总结:简单配置用 Preferences,复杂关系用 RDB。

Preferences 实战:轻量 KV 存储

场景:保存用户设置(主题、字号、通知开关)。

完整代码

import{preferences}from'@kit.ArkData'import{Context}from'@kit.AbilityKit'constPREF_NAME='user_settings'classPrefManager{privatepref:preferences.Preferences|null=nullasyncinit(context:Context){this.pref=awaitpreferences.getPreferences(context,PREF_NAME)}asyncput(key:string,value:preferences.ValueType){if(!this.pref)returnawaitthis.pref.put(key,value)awaitthis.pref.flush()}asyncget(key:string,defaultValue:preferences.ValueType):Promise<preferences.ValueType>{if(!this.pref)returndefaultValuereturnawaitthis.pref.get(key,defaultValue)}asyncremove(key:string){if(!this.pref)returnawaitthis.pref.delete(key)awaitthis.pref.flush()}asyncclear(){if(!this.pref)returnawaitthis.pref.clear()awaitthis.pref.flush()}}exportconstprefManager=newPrefManager()

API 逐个讲解

  • getPreferences(context, name):根据 name 获取 Preferences 实例。同一个 name 全局只有一个实例,系统会缓存。
  • put(key, value):写入键值对。数据先写到内存缓存,调用flush()才真正写入磁盘。
  • flush()这个必须调!不调 flush,应用崩溃数据就丢了。put之后紧跟flush是标准操作。
  • get(key, defaultValue):读取数据。没有这个 key 就返回默认值,不会报错。
  • delete(key):删除指定 key,也要跟flush()
  • clear():清空所有数据,慎用。

使用示例

// 初始化(在 EntryAbility.onCreate 中)awaitprefManager.init(this.context)// 存储用户设置awaitprefManager.put('theme','dark')awaitprefManager.put('fontSize',18)awaitprefManager.put('notifications',true)// 读取consttheme=awaitprefManager.get('theme','light')asstringconstfontSize=awaitprefManager.get('fontSize',14)asnumberconstnotify=awaitprefManager.get('notifications',false)asboolean

就这么简单。存配置项、开关、标记,Preferences 真的够了。

RDB 实战:关系型数据库 CRUD

场景:做一个记账 App,需要存账单数据(金额、分类、日期、备注)。

完整代码

import{relationalStore}from'@kit.ArkData'import{Context}from'@kit.AbilityKit'constDB_NAME='bill.db'constSTORE_CONFIG:relationalStore.StoreConfig={name:DB_NAME,securityLevel:relationalStore.SecurityLevel.S1}constCREATE_TABLE_SQL=`CREATE TABLE IF NOT EXISTS bills ( id INTEGER PRIMARY KEY AUTOINCREMENT, amount REAL NOT NULL, category TEXT NOT NULL, date TEXT NOT NULL, note TEXT, created_at INTEGER )`classBillDB{privaterdbStore:relationalStore.RdbStore|null=nullasyncinit(context:Context){this.rdbStore=awaitrelationalStore.getRdbStore(context,STORE_CONFIG)awaitthis.rdbStore.executeSql(CREATE_TABLE_SQL)}asyncinsert(bill:BillItem):Promise<number>{if(!this.rdbStore)return-1constvalues:relationalStore.ValuesBucket={amount:bill.amount,category:bill.category,date:bill.date,note:bill.note||'',created_at:Date.now()}constrowId=awaitthis.rdbStore.insert('bills',values)returnrowId}asyncqueryByDate(startDate:string,endDate:string):Promise<BillItem[]>{if(!this.rdbStore)return[]constpredicates=newrelationalStore.RdbPredicates('bills')predicates.between('date',startDate,endDate).orderByDesc('date')constresultSet=awaitthis.rdbStore.query(predicates)returnthis.resultSetToList(resultSet)}asyncdeleteById(id:number):Promise<number>{if(!this.rdbStore)return0constpredicates=newrelationalStore.RdbPredicates('bills')predicates.equalTo('id',id)returnawaitthis.rdbStore.delete(predicates)}asyncupdateById(id:number,bill:Partial<BillItem>):Promise<number>{if(!this.rdbStore)return0constvalues:relationalStore.ValuesBucket={}if(bill.amount!==undefined)values.amount=bill.amountif(bill.category!==undefined)values.category=bill.categoryif(bill.note!==undefined)values.note=bill.noteconstpredicates=newrelationalStore.RdbPredicates('bills')predicates.equalTo('id',id)returnawaitthis.rdbStore.update(values,predicates)}privateresultSetToList(resultSet:relationalStore.ResultSet):BillItem[]{constlist:BillItem[]=[]if(resultSet.rowCount<=0){resultSet.close()returnlist}resultSet.goToFirstRow()do{constbill:BillItem={id:resultSet.getLong(resultSet.getColumnIndex('id')),amount:resultSet.getDouble(resultSet.getColumnIndex('amount')),category:resultSet.getString(resultSet.getColumnIndex('category')),date:resultSet.getString(resultSet.getColumnIndex('date')),note:resultSet.getString(resultSet.getColumnIndex('note'))}list.push(bill)}while(resultSet.goToNextRow())resultSet.close()returnlist}}interfaceBillItem{id?:numberamount:numbercategory:stringdate:stringnote?:string}exportconstbillDB=newBillDB()

SQL 和 API 讲解

建表 SQL:

  • INTEGER PRIMARY KEY AUTOINCREMENT:主键自增,插入时不用手动指定 id
  • REAL NOT NULL:金额用浮点类型,不允许为空
  • TEXT NOT NULL:分类和日期用字符串,不允许为空
  • TEXT:备注可空

CRUD 操作:

  • insert(tableName, valuesBucket)—— valuesBucket 是键值对,key 对应列名
  • RdbPredicates构建查询条件,between做范围查询,equalTo做精确匹配
  • update(valuesBucket, predicates)—— 只更新 valuesBucket 里的字段
  • delete(predicates)—— 按 predicates 条件删除

ResultSet 解析:

这是 RDB 最烦的部分。query返回ResultSet,要手动遍历、按列名取值、最后close()忘 close 会内存泄漏,我踩过。

性能对比数据

实测数据(1000 条记录,中端手机):

操作PreferencesRDB
写入 1 条< 1ms2-5ms
读取 1 条< 1ms3-8ms
写入 100 条10-20ms50-100ms
条件查询 100 条不支持10-30ms
全量查询 1000 条不现实50-100ms

Preferences 读写速度碾压 RDB,因为数据全在内存里。但一旦涉及条件查询、排序、关联,Preferences 直接歇菜。

选型建议

别纠结了,按这张决策表选:

你的需求选哪个理由
用户设置、开关、标记Preferences轻量快速,KV 就够了
登录态缓存Preferences就一个 token,用啥数据库
引导页是否展示过Preferences一个 boolean 搞定
购物车(< 50 条)Preferences数据少,简单
购物车(> 50 条)RDB数据多了需要筛选排序
聊天记录RDB数据量大,需要按时间查询
商品数据RDB需要分类筛选排序
订单记录RDB多表关联,复杂查询
收藏列表(> 100 条)RDB需要分页查询

我的原则:能 Preferences 就不 RDB,数据一复杂就别硬撑。

两个方案也不冲突,可以在同一个项目里都用——设置用 Preferences,业务数据用 RDB,各司其职。

写在最后

数据持久化没有银弹,选型就是看场景。Preferences 是一把快刀,削苹果削黄瓜都好用,但别拿它砍树。RDB 是电锯,砍树一流,削苹果就太费劲了。

搞清楚自己要存什么、怎么查、数据量多大,答案就出来了。

http://www.jsqmd.com/news/1250592/

相关文章:

  • 广州财税服务行业GEO优化公司选型指南丨生成式引擎优化服务商深度测评2026本地榜单解析 - 企业新闻快传
  • 59.嵌入式C语言高级宏定义实战:多行宏、字符串化与符号拼接
  • 【RT-DETR涨点改进】TGRS 2026 | 特征融合改进篇 |引入CDSF跨域协同融合模块,增强特征互补性与语义一致性,助力高光谱目标检测、遥感目标检测、多模态融合目标检测任务,高效涨点
  • NTC 热敏电阻全解:计算、曲线读数、跨品牌替换一次讲透
  • 2026年陕西学化妆选校攻略:正规权威机构甄别与避坑指南 - 产业观察报
  • 2026 年现阶段泾县评价高的膜结构张拉膜施工厂家推荐公司怎么联系,颠覆认知:膜结构张拉,比你想象的更省钱! - 行业鉴选官
  • 椰林海鲜码头企业新愿景是什么?:良正基业永昌 - 18002239949
  • 智能装备整机采购指南,2026转矩传感器品牌排行更新,广东犸力销量排名逐年攀升 - 品牌速递
  • ESC框架知识点
  • D2D商业化初期的博弈:成本、价格与增长的“三重奏”
  • 沁园春·数智潮
  • 汽车轮重检测仪品牌排名汇总揭晓,浙江润鑫头部品牌一致好评实力获市场认可 - 品牌速递
  • 老板必看!AI落地就做这两件事,ROI瞬间翻倍!
  • 联合利华CIO任命揭示企业数字化转型战略
  • 多元微积分
  • 【RT-DETR涨点改进】CVPR 2026 | 卷积创新改进篇 | 轻量化改进!引入YOLO-ULM中轻量D3C2f轻量化模块,含二次创新模块,5种创新改进点,助力目标检测任务,高效涨点
  • 浪琴天津售后服务中心2026年7月最新地址与客服热线重磅发布 - 浪琴服务中心
  • 2026西安美甲创业培训学校选型盘点及避坑指南:哪家正规靠谱且适配本地创业需求?附正规机构实力解析 - 行业观察网
  • LLM-Pruner: On the Structural Pruning of Large Language Models 解读
  • Android Camera Sensor Mode 分类与选择详解
  • [godot ]
  • linux_x86_64 虚拟机交叉编译aarch64平台qt源码
  • 一位直博生的 AI 编程困境,会语法,却写不出项目?|AI悦创VibeCoding/Python一对一辅导分享
  • HarmonyOS7 传感器开发:加速度计做个摇一摇,原来这么简单
  • 2026 年 7 月新发布:常熟有实力的无机纤维喷涂工程公司电话实力厂家推荐,揭秘:高强度无机纤维喷涂的秘密工厂电话曝光-佑辰隔音保温 - 鉴选官
  • WordPress国际化多语言建站插件:解决Hreflang标签的3个报错
  • 小程序自动部署埋点
  • AI 驱动的 Rust 代码生成:从接口描述自动生成样板代码的边界探索
  • 企业级NAS建设方案
  • 2026十大生产制造ERP:警惕出货量误导,基于MES集成度与工单闭环逻辑重排