
文章目录前言两种方案定位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}fromkit.ArkDataimport{Context}fromkit.AbilityKitconstPREF_NAMEuser_settingsclassPrefManager{privatepref:preferences.Preferences|nullnullasyncinit(context:Context){this.prefawaitpreferences.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):Promisepreferences.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()}}exportconstprefManagernewPrefManager()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)// 读取constthemeawaitprefManager.get(theme,light)asstringconstfontSizeawaitprefManager.get(fontSize,14)asnumberconstnotifyawaitprefManager.get(notifications,false)asboolean就这么简单。存配置项、开关、标记Preferences 真的够了。RDB 实战关系型数据库 CRUD场景做一个记账 App需要存账单数据金额、分类、日期、备注。完整代码import{relationalStore}fromkit.ArkDataimport{Context}fromkit.AbilityKitconstDB_NAMEbill.dbconstSTORE_CONFIG:relationalStore.StoreConfig{name:DB_NAME,securityLevel:relationalStore.SecurityLevel.S1}constCREATE_TABLE_SQLCREATE 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|nullnullasyncinit(context:Context){this.rdbStoreawaitrelationalStore.getRdbStore(context,STORE_CONFIG)awaitthis.rdbStore.executeSql(CREATE_TABLE_SQL)}asyncinsert(bill:BillItem):Promisenumber{if(!this.rdbStore)return-1constvalues:relationalStore.ValuesBucket{amount:bill.amount,category:bill.category,date:bill.date,note:bill.note||,created_at:Date.now()}constrowIdawaitthis.rdbStore.insert(bills,values)returnrowId}asyncqueryByDate(startDate:string,endDate:string):PromiseBillItem[]{if(!this.rdbStore)return[]constpredicatesnewrelationalStore.RdbPredicates(bills)predicates.between(date,startDate,endDate).orderByDesc(date)constresultSetawaitthis.rdbStore.query(predicates)returnthis.resultSetToList(resultSet)}asyncdeleteById(id:number):Promisenumber{if(!this.rdbStore)return0constpredicatesnewrelationalStore.RdbPredicates(bills)predicates.equalTo(id,id)returnawaitthis.rdbStore.delete(predicates)}asyncupdateById(id:number,bill:PartialBillItem):Promisenumber{if(!this.rdbStore)return0constvalues:relationalStore.ValuesBucket{}if(bill.amount!undefined)values.amountbill.amountif(bill.category!undefined)values.categorybill.categoryif(bill.note!undefined)values.notebill.noteconstpredicatesnewrelationalStore.RdbPredicates(bills)predicates.equalTo(id,id)returnawaitthis.rdbStore.update(values,predicates)}privateresultSetToList(resultSet:relationalStore.ResultSet):BillItem[]{constlist:BillItem[][]if(resultSet.rowCount0){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}exportconstbillDBnewBillDB()SQL 和 API 讲解建表 SQLINTEGER PRIMARY KEY AUTOINCREMENT主键自增插入时不用手动指定 idREAL 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-100msPreferences 读写速度碾压 RDB因为数据全在内存里。但一旦涉及条件查询、排序、关联Preferences 直接歇菜。选型建议别纠结了按这张决策表选你的需求选哪个理由用户设置、开关、标记Preferences轻量快速KV 就够了登录态缓存Preferences就一个 token用啥数据库引导页是否展示过Preferences一个 boolean 搞定购物车 50 条Preferences数据少简单购物车 50 条RDB数据多了需要筛选排序聊天记录RDB数据量大需要按时间查询商品数据RDB需要分类筛选排序订单记录RDB多表关联复杂查询收藏列表 100 条RDB需要分页查询我的原则能 Preferences 就不 RDB数据一复杂就别硬撑。两个方案也不冲突可以在同一个项目里都用——设置用 Preferences业务数据用 RDB各司其职。写在最后数据持久化没有银弹选型就是看场景。Preferences 是一把快刀削苹果削黄瓜都好用但别拿它砍树。RDB 是电锯砍树一流削苹果就太费劲了。搞清楚自己要存什么、怎么查、数据量多大答案就出来了。