HarmonyOS鸿蒙实战应用6:随手账本——Preferences本地持久化
·
引言
前几篇数据都是存在内存里的——App重启数据就丢了。本篇用 @ohos.data.preferences 实现数据的本地持久化,确保数据在App重启后仍然存在。
先看本次要持久化的数据——新增一笔 66 元交通账单:

一、持久化方案
二、实现持久化 Store
// store/PersistentBillStore.ts
import preferences from '@ohos.data.preferences';
import { BillItem, BillItemInput, BillType, BillCategory } from '../model/BillItem';
export class PersistentBillStore {
private bills: BillItem[] = [];
private prefs: preferences.Preferences | null = null;
private isReady: boolean = false;
private pendingQueue: Array<() => void> = [];
// 初始化:加载Preferences
async init(context: Context): Promise<void> {
this.prefs = await preferences.getPreferences(context, 'easy_account');
// 从Preferences读取序列化数据
const jsonStr = this.prefs.getSync('bills_data', '[]') as string;
try {
this.bills = JSON.parse(jsonStr);
} catch {
this.bills = [];
}
this.isReady = true;
// 执行等待队列
this.pendingQueue.forEach(fn => fn());
this.pendingQueue = [];
}
// 保存到磁盘
private save(): void {
if (!this.prefs) return;
const jsonStr = JSON.stringify(this.bills);
this.prefs.putSync('bills_data', jsonStr);
this.prefs.flushSync();
}
// 等待初始化完成
private ensureReady(): Promise<void> {
if (this.isReady) return Promise.resolve();
return new Promise(resolve => {
this.pendingQueue.push(() => resolve());
});
}
// 增删改查
async getAll(): Promise<BillItem[]> {
await this.ensureReady();
return [...this.bills];
}
getById(id: string): BillItem | undefined {
return this.bills.find(b => b.id === id);
}
add(bill: BillItemInput): BillItem {
// ArkTS 不支持对象展开,逐字段构造
const newBill: BillItem = {
type: bill.type,
category: bill.category,
amount: bill.amount,
note: bill.note,
date: bill.date,
id: Date.now().toString(36) + Math.random().toString(36).substring(2, 7),
createTime: Date.now()
};
this.bills.unshift(newBill);
this.save();
return newBill;
}
update(id: string, updates: Partial<BillItem>): boolean {
const idx = this.bills.findIndex(b => b.id === id);
if (idx === -1) return false;
// ArkTS 不支持对象展开合并,改为逐字段判断更新
if (updates.type !== undefined) {
this.bills[idx].type = updates.type;
}
if (updates.category !== undefined) {
this.bills[idx].category = updates.category;
}
if (updates.amount !== undefined) {
this.bills[idx].amount = updates.amount;
}
if (updates.note !== undefined) {
this.bills[idx].note = updates.note;
}
if (updates.date !== undefined) {
this.bills[idx].date = updates.date;
}
this.save();
return true;
}
delete(id: string): boolean {
const idx = this.bills.findIndex(b => b.id === id);
if (idx === -1) return false;
this.bills.splice(idx, 1);
this.save();
return true;
}
// 数据迁移(版本升级时)
migrateIfNeeded(): void {
const version = this.prefs?.getSync('data_version', 1) as number;
if (version < 2) {
// v1 → v2 迁移逻辑
this.prefs?.putSync('data_version', 2);
this.prefs?.flushSync();
}
}
}
// 导出全局单例
export const persistentStore = new PersistentBillStore();
三、在 App 入口初始化
// entryability/EntryAbility.ets
import { persistentStore } from '../store/PersistentBillStore';
export default class EntryAbility extends UIAbility {
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
// 在 Ability 创建时初始化持久化
await persistentStore.init(this.context);
console.log('账单数据加载完成');
}
}
四、首页加载持久化数据
// pages/HomePage.ets 修改
import { persistentStore } from '../store/PersistentBillStore';
@Entry
@Component
struct HomePage {
@State bills: BillItem[] = [];
@State isLoading: boolean = true;
async aboutToAppear() {
this.isLoading = true;
// 从持久化存储加载数据
this.bills = await persistentStore.getAll();
this.isLoading = false;
}
build() {
Stack() {
if (this.isLoading) {
// 加载中显示骨架屏
LoadingPlaceholder()
} else {
// 正常内容
this.MainContent()
}
}
}
}
新增成功后首页同步更新——支出从 ¥434 变为 ¥500,新账单出现在列表顶部:

五、迁移内存数据
如果你之前用内存版 BillStore 已经有数据了,需要一个迁移入口:
// utils/MigrateData.ts
import { billStore } from '../store/BillStore';
import { persistentStore } from '../store/PersistentBillStore';
export async function migrateFromMemory() {
const memoryBills = billStore.getAll();
if (memoryBills.length === 0) return;
const existBills = await persistentStore.getAll();
if (existBills.length > 0) return; // 已经有数据了
// 将内存数据写入持久化
for (const bill of memoryBills) {
persistentStore.add({
type: bill.type,
category: bill.category,
amount: bill.amount,
note: bill.note,
date: bill.date
});
}
console.log(`已迁移 ${memoryBills.length} 条数据到持久化存储`);
}
六、数据导出验证
调试时可以查看 Preferences 存储的文件:
沙箱路径: /data/storage/el2/base/preferences/easy_account.xml
打开后能看到序列化的 JSON 数据:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<preferences>
<string name="bills_data">[{"id":"xxx","type":"expense",...}]</string>
<int name="data_version" value="2"/>
</preferences>
完全关闭 App 后重新启动——66 元交通账单、¥500 支出、¥15000 收入原样恢复,数据真正"存得住"了:

七、异常处理
// 保存失败时重试
private saveWithRetry(maxRetries = 3): boolean {
for (let i = 0; i < maxRetries; i++) {
try {
this.save();
return true;
} catch (e) {
console.error(`保存失败(第${i + 1}次):`, e.message);
}
}
console.error('保存失败,已达最大重试次数');
return false;
}
总结
本篇实现了:
- Preferences 持久化:增删改自动同步到磁盘
- 异步初始化:Ability 创建时加载数据
- 版本迁移:支持数据格式升级
- 异常处理:保存失败自动重试
- 数据验证:可直接查看沙箱 XML 文件
从这篇开始,随手账本的数据就真正"存得住"了。下篇实现数据导出与分享功能。
更多推荐



所有评论(0)