鸿蒙数据持久化三板斧:Preferences、RDB、分布式数据一文搞定,告别数据丢失
📝【鸿蒙NEXT数据持久化实战指南】 本文系统介绍了鸿蒙应用开发的三大数据持久化方案: 1️⃣ Preferences轻量存储 键值对存储,适合配置/开关等简单数据 内存缓存加速读取,需手动flush持久化 典型场景:用户设置、登录Token 2️⃣ RDB关系型数据库 基于SQLite的结构化存储 支持复杂查询与事务操作 典型场景:业务数据、聊天记录 3️⃣ 分布式KV存储 鸿蒙特色跨设备同步
📖 鸿蒙NEXT开发实战系列 | 第21篇 | 数据篇 🎯 适合人群:有鸿蒙基础的开发者 ⏰ 阅读时间:约15分钟 | 💻 开发环境:DevEco Studio 5.0+
⬅️ 上一篇:20-网络篇-网络请求与数据加载
➡️ 下一篇:22-数据篇-文件管理与沙箱机制
📑 目录
前言
你是否遇到过这样的问题:用户设置了一个深色主题,退出App后再打开,又变回了浅色主题?或者用户填写了一半的表单,App切到后台被系统回收,回来后数据全没了?
这些问题的根源都是:数据没有持久化。
在鸿蒙开发中,数据持久化有三大利器,我称之为"三板斧":
-
Preferences - 轻量级键值存储,适合简单配置
-
RDB - 关系型数据库,适合结构化数据
-
分布式数据管理 - 跨设备数据同步
今天我们就来系统学习这三种方案,让你的App数据不再"失忆"!
一、数据持久化概述
1.1 为什么需要持久化
在鸿蒙应用中,变量存储在内存中,进程结束后内存被释放,数据自然就没了。持久化就是把数据从内存"搬到"磁盘上,这样即使App关闭、设备重启,数据依然存在。
1.2 鸿蒙存储体系
鸿蒙提供了多层次的存储方案:
|
存储方式 |
特点 |
适用场景 |
|---|---|---|
|
内存变量 |
速度快,进程结束即丢失 |
临时数据 |
|
Preferences |
轻量KV,适合小数据 |
配置、开关 |
|
RDB |
关系型,支持SQL |
结构化业务数据 |
|
分布式KV |
跨设备同步 |
多设备协同 |
|
文件存储 |
任意格式 |
大文件、媒体 |
二、Preferences:轻量级键值存储
2.1 什么是Preferences
Preferences(首选项)是一个轻量级的键值对存储方案,类似于Android的SharedPreferences或iOS的UserDefaults。它的特点是:
-
简单易用:API简洁,几行代码搞定
-
内存缓存:首次加载后缓存在内存,读取飞快
-
适合小数据:建议存储数据量不超过几KB
典型应用场景:
-
用户设置(主题、语言、字号)
-
开关状态(是否首次启动、通知开关)
-
登录Token、用户信息缓存
2.2 基本使用
import { preferences } from '@kit.ArkData';
// 获取Preferences实例
const store = await preferences.getPreferences(context, 'myStore');
// 写入数据
await store.put('key', 'value');
await store.flush(); // 持久化到磁盘
// 读取数据
const value = await store.get('key', 'defaultValue');
// 删除数据
await store.delete('key');
await store.flush();
2.3 完整代码示例
下面是一个用户设置页面的完整示例,演示如何用Preferences保存用户偏好:
import { preferences } from '@kit.ArkData';
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
// 定义设置项接口
interface UserSettings {
theme: 'light' | 'dark';
fontSize: number;
notificationEnabled: boolean;
language: string;
}
@Entry
@Component
struct SettingsPage {
@State settings: UserSettings = {
theme: 'light',
fontSize: 16,
notificationEnabled: true,
language: 'zh-CN'
};
@State isLoading: boolean = true;
// 获取Preferences实例
private store: preferences.Preferences | null = null;
async aboutToAppear() {
await this.loadSettings();
}
// 加载设置
async loadSettings() {
try {
const context = getContext(this);
this.store = await preferences.getPreferences(context, 'userSettings');
// 读取各项设置,带默认值
this.settings = {
theme: (await this.store.get('theme', 'light')) as 'light' | 'dark',
fontSize: (await this.store.get('fontSize', 16)) as number,
notificationEnabled: (await this.store.get('notificationEnabled', true)) as boolean,
language: (await this.store.get('language', 'zh-CN')) as string
};
this.isLoading = false;
} catch (err) {
hilog.error(0x0000, 'SettingsPage', `加载设置失败: ${err}`);
}
}
// 保存设置
async saveSettings() {
if (!this.store) return;
try {
await this.store.put('theme', this.settings.theme);
await this.store.put('fontSize', this.settings.fontSize);
await this.store.put('notificationEnabled', this.settings.notificationEnabled);
await this.store.put('language', this.settings.language);
// 关键:调用flush将内存数据写入磁盘
await this.store.flush();
hilog.info(0x0000, 'SettingsPage', '设置保存成功');
} catch (err) {
hilog.error(0x0000, 'SettingsPage', `保存设置失败: ${err}`);
}
}
build() {
Column() {
if (this.isLoading) {
LoadingProgress().width(50).height(50)
} else {
// 主题切换
Row() {
Text('深色模式').fontSize(16)
Toggle({ type: ToggleType.Switch, isOn: this.settings.theme === 'dark' })
.onChange(async (isOn) => {
this.settings.theme = isOn ? 'dark' : 'light';
await this.saveSettings();
})
}
.width('100%')
.padding(16)
.justifyContent(FlexAlign.SpaceBetween)
// 字号调节
Row() {
Text('字体大小').fontSize(16)
Slider({
value: this.settings.fontSize,
min: 12,
max: 24,
step: 2
})
.onChange(async (value) => {
this.settings.fontSize = value;
await this.saveSettings();
})
.width(200)
}
.width('100%')
.padding(16)
.justifyContent(FlexAlign.SpaceBetween)
// 通知开关
Row() {
Text('推送通知').fontSize(16)
Toggle({ type: ToggleType.Switch, isOn: this.settings.notificationEnabled })
.onChange(async (isOn) => {
this.settings.notificationEnabled = isOn;
await this.saveSettings();
})
}
.width('100%')
.padding(16)
.justifyContent(FlexAlign.SpaceBetween)
}
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
关键点说明:
-
使用
preferences.getPreferences()获取实例,同一个name共享同一个实例 -
写入后必须调用
flush()才能持久化到磁盘 -
读取时可以设置默认值,防止key不存在时报错
三、RDB:关系型数据库
3.1 什么是RDB
RDB(Relational Database)是鸿蒙提供的关系型数据库,底层基于SQLite。相比Preferences,它适合存储更复杂、更大量的结构化数据。
典型应用场景:
-
聊天记录、消息列表
-
用户数据、订单信息
-
任何需要查询、排序、筛选的数据
3.2 基本使用
import { relationalStore } from '@kit.ArkData';
// 创建/获取数据库
const store = await relationalStore.getRdbStore(context, {
name: 'myDB.db',
securityLevel: relationalStore.SecurityLevel.S1
});
// 创建表
await store.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');
// 插入数据
const valueBucket: relationalStore.ValueBucket = { name: '张三', age: 25 };
const rowId = await store.insert('users', valueBucket);
// 查询数据
const predicates = new relationalStore.RdbPredicates('users');
predicates.equalTo('name', '张三');
const resultSet = await store.query(predicates, ['id', 'name', 'age']);
// 更新数据
const updateBucket: relationalStore.ValueBucket = { age: 26 };
predicates.equalTo('id', rowId);
await store.update(updateBucket, predicates);
// 删除数据
await store.delete(predicates);
3.3 完整代码示例
下面是一个待办事项App的完整示例:
import { relationalStore } from '@kit.ArkData';
import { hilog } from '@kit.PerformanceAnalysisKit';
// 定义Todo数据模型
interface TodoItem {
id?: number;
title: string;
completed: boolean;
createTime: number;
}
@Entry
@Component
struct TodoApp {
@State todoList: TodoItem[] = [];
@State newTodoTitle: string = '';
private store: relationalStore.RdbStore | null = null;
async aboutToAppear() {
await this.initDatabase();
await this.loadTodos();
}
// 初始化数据库
async initDatabase() {
try {
const context = getContext(this);
const config: relationalStore.StoreConfig = {
name: 'todo.db',
securityLevel: relationalStore.SecurityLevel.S1
};
this.store = await relationalStore.getRdbStore(context, config);
// 创建表
const createTableSql = `
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
completed INTEGER DEFAULT 0,
createTime INTEGER
)
`;
await this.store.executeSql(createTableSql);
hilog.info(0x0000, 'TodoApp', '数据库初始化成功');
} catch (err) {
hilog.error(0x0000, 'TodoApp', `数据库初始化失败: ${err}`);
}
}
// 加载待办列表
async loadTodos() {
if (!this.store) return;
try {
const predicates = new relationalStore.RdbPredicates('todos');
predicates.orderByDesc('createTime');
const resultSet = await this.store.query(predicates, ['id', 'title', 'completed', 'createTime']);
const todos: TodoItem[] = [];
while (resultSet.goToNextRow()) {
todos.push({
id: resultSet.getLong(resultSet.getColumnIndex('id')),
title: resultSet.getString(resultSet.getColumnIndex('title')),
completed: resultSet.getLong(resultSet.getColumnIndex('completed')) === 1,
createTime: resultSet.getLong(resultSet.getColumnIndex('createTime'))
});
}
resultSet.close();
this.todoList = todos;
} catch (err) {
hilog.error(0x0000, 'TodoApp', `加载待办失败: ${err}`);
}
}
// 添加待办
async addTodo() {
if (!this.store || !this.newTodoTitle.trim()) return;
try {
const valueBucket: relationalStore.ValueBucket = {
title: this.newTodoTitle.trim(),
completed: 0,
createTime: Date.now()
};
await this.store.insert('todos', valueBucket);
this.newTodoTitle = '';
await this.loadTodos();
} catch (err) {
hilog.error(0x0000, 'TodoApp', `添加待办失败: ${err}`);
}
}
// 切换完成状态
async toggleTodo(item: TodoItem) {
if (!this.store || !item.id) return;
try {
const predicates = new relationalStore.RdbPredicates('todos');
predicates.equalTo('id', item.id);
const valueBucket: relationalStore.ValueBucket = {
completed: item.completed ? 0 : 1
};
await this.store.update(valueBucket, predicates);
await this.loadTodos();
} catch (err) {
hilog.error(0x0000, 'TodoApp', `更新待办失败: ${err}`);
}
}
// 删除待办
async deleteTodo(id: number) {
if (!this.store) return;
try {
const predicates = new relationalStore.RdbPredicates('todos');
predicates.equalTo('id', id);
await this.store.delete(predicates);
await this.loadTodos();
} catch (err) {
hilog.error(0x0000, 'TodoApp', `删除待办失败: ${err}`);
}
}
build() {
Column() {
// 标题
Text('待办事项')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.padding(16)
// 输入区域
Row() {
TextInput({ placeholder: '添加新的待办事项', text: this.newTodoTitle })
.onChange((value) => this.newTodoTitle = value)
.layoutWeight(1)
.margin({ right: 8 })
Button('添加')
.onClick(() => this.addTodo())
.width(80)
}
.padding(16)
// 待办列表
List({ space: 8 }) {
ForEach(this.todoList, (item: TodoItem) => {
ListItem() {
Row() {
Checkbox()
.select(item.completed)
.onChange(() => this.toggleTodo(item))
.margin({ right: 12 })
Text(item.title)
.fontSize(16)
.decoration({ type: item.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
.fontColor(item.completed ? '#999999' : '#333333')
.layoutWeight(1)
Button('删除')
.type(ButtonType.Circle)
.width(32)
.height(32)
.fontSize(12)
.onClick(() => this.deleteTodo(item.id!))
}
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
}
})
}
.padding(16)
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F0F0F0')
}
}
关键点说明:
-
创建表时使用
executeSql()执行SQL语句 -
使用
RdbPredicates构建查询条件 -
查询结果需要手动遍历
ResultSet获取数据 -
记得关闭
ResultSet避免资源泄漏
四、分布式数据管理
4.1 什么是分布式KV
分布式数据管理是鸿蒙的特色能力,可以实现数据在多个设备间自动同步。想象一下:你在手机上设置了一个闹钟,平板上也能自动看到这个闹钟,这就是分布式数据的魔力。
分布式KV(Key-Value)是其中最常用的API,适合在多设备间同步简单的键值数据。
典型应用场景:
-
跨设备同步用户设置
-
多设备协同办公数据
-
分布式游戏存档
4.2 基本使用
import { distributedKVStore } from '@kit.ArkData';
// 创建KV管理器
const kvManager = distributedKVStore.createKVManager({
bundleName: 'com.example.myapp',
userInfo: { userId: '0', userType: distributedKVStore.UserType.SAME_USER_ID }
});
// 获取KVStore
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true
};
const kvStore = await kvManager.getKVStore('myStoreId', options);
// 写入数据
const entry: distributedKVStore.Entry = { key: 'theme', value: 'dark' };
await kvStore.put(entry.key, entry.value);
// 读取数据
const value = await kvStore.get('theme');
// 订阅数据变化
kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
console.log('数据变化:', data);
});
4.3 完整代码示例
下面是一个跨设备同步收藏列表的示例:
import { distributedKVStore } from '@kit.ArkData';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
interface FavoriteItem {
id: string;
title: string;
url: string;
addTime: number;
}
@Entry
@Component
struct DistributedFavorites {
@State favorites: FavoriteItem[] = [];
@State syncStatus: string = '未连接';
private kvStore: distributedKVStore.SingleKVStore | null = null;
private kvManager: distributedKVStore.KVManager | null = null;
async aboutToAppear() {
await this.initDistributedStore();
}
// 初始化分布式存储
async initDistributedStore() {
try {
// 创建KV管理器
const managerConfig: distributedKVStore.ManagerConfig = {
bundleName: 'com.example.myapp',
userInfo: {
userId: '0',
userType: distributedKVStore.UserType.SAME_USER_ID
}
};
this.kvManager = distributedKVStore.createKVManager(managerConfig);
// 获取KVStore
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true, // 开启自动同步
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION
};
this.kvStore = await this.kvManager.getKVStore('favorites_store', options);
this.syncStatus = '已连接';
// 订阅数据变化
this.subscribeDataChange();
// 订阅设备上下线
this.subscribeDeviceChange();
// 加载数据
await this.loadFavorites();
hilog.info(0x0000, 'DistributedFavorites', '分布式存储初始化成功');
} catch (err) {
const error = err as BusinessError;
hilog.error(0x0000, 'DistributedFavorites', `初始化失败: ${error.message}`);
}
}
// 订阅数据变化
subscribeDataChange() {
if (!this.kvStore) return;
this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, async (data) => {
hilog.info(0x0000, 'DistributedFavorites', `收到数据变更: ${JSON.stringify(data)}`);
await this.loadFavorites();
});
}
// 订询设备变化
subscribeDeviceChange() {
if (!this.kvManager) return;
this.kvManager.on('distributedDataServiceDie', () => {
hilog.warn(0x0000, 'DistributedFavorites', '分布式服务断开');
this.syncStatus = '已断开';
});
}
// 加载收藏列表
async loadFavorites() {
if (!this.kvStore) return;
try {
const entries = await this.kvStore.getEntries('favorite_');
const items: FavoriteItem[] = [];
for (const entry of entries) {
if (entry.value) {
try {
const item = JSON.parse(entry.value as string) as FavoriteItem;
items.push(item);
} catch (e) {
// 忽略解析失败的数据
}
}
}
// 按时间倒序排列
items.sort((a, b) => b.addTime - a.addTime);
this.favorites = items;
} catch (err) {
hilog.error(0x0000, 'DistributedFavorites', `加载失败: ${err}`);
}
}
// 添加收藏
async addFavorite() {
if (!this.kvStore) return;
const newItem: FavoriteItem = {
id: `fav_${Date.now()}`,
title: `收藏项 ${this.favorites.length + 1}`,
url: `https://example.com/${this.favorites.length + 1}`,
addTime: Date.now()
};
try {
// 使用前缀+ID作为key,便于批量查询
await this.kvStore.put(`favorite_${newItem.id}`, JSON.stringify(newItem));
await this.loadFavorites();
hilog.info(0x0000, 'DistributedFavorites', '添加收藏成功,已同步到其他设备');
} catch (err) {
hilog.error(0x0000, 'DistributedFavorites', `添加收藏失败: ${err}`);
}
}
// 删除收藏
async deleteFavorite(id: string) {
if (!this.kvStore) return;
try {
await this.kvStore.delete(`favorite_${id}`);
await this.loadFavorites();
} catch (err) {
hilog.error(0x0000, 'DistributedFavorites', `删除收藏失败: ${err}`);
}
}
// 手动同步到指定设备
async syncToDevice(deviceId: string) {
if (!this.kvStore) return;
try {
await this.kvStore.sync([deviceId], distributedKVStore.SyncMode.PUSH_PULL);
hilog.info(0x0000, 'DistributedFavorites', `已触发同步到设备: ${deviceId}`);
} catch (err) {
hilog.error(0x0000, 'DistributedFavorites', `同步失败: ${err}`);
}
}
build() {
Column() {
// 标题栏
Row() {
Text('分布式收藏夹')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Blank()
Text(this.syncStatus)
.fontSize(12)
.fontColor(this.syncStatus === '已连接' ? '#4CAF50' : '#FF5722')
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.backgroundColor(this.syncStatus === '已连接' ? '#E8F5E9' : '#FBE9E7')
.borderRadius(12)
}
.width('100%')
.padding(16)
// 添加按钮
Button('添加收藏(自动同步到其他设备)')
.width('90%')
.margin({ bottom: 16 })
.onClick(() => this.addFavorite())
// 收藏列表
List({ space: 8 }) {
ForEach(this.favorites, (item: FavoriteItem) => {
ListItem() {
Column() {
Text(item.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text(item.url)
.fontSize(12)
.fontColor('#666666')
.margin({ top: 4 })
Text(new Date(item.addTime).toLocaleString())
.fontSize(11)
.fontColor('#999999')
.margin({ top: 4 })
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.gesture(
LongPressGesture()
.onAction(() => this.deleteFavorite(item.id))
)
}
})
}
.padding(16)
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
关键点说明:
-
分布式数据需要在多个设备上登录同一华为账号
-
autoSync: true可以自动同步,也可以手动调用sync() -
使用
on('dataChange')监听数据变化,实现实时更新 -
key的设计很重要,使用前缀便于批量查询
五、三大方案对比与选型
5.1 对比表格
|
特性 |
Preferences |
RDB |
分布式KV |
|---|---|---|---|
|
数据类型 |
键值对(基础类型) |
结构化表格数据 |
键值对(基础类型) |
|
数据量 |
小(建议 < 几KB) |
大(支持百万级) |
中等(建议 < 10MB) |
|
查询能力 |
仅按key查询 |
支持SQL查询 |
仅按key/前缀查询 |
|
跨设备 |
不支持 |
不支持 |
支持 |
|
性能 |
极快(内存缓存) |
快 |
中等(含同步开销) |
|
使用复杂度 |
低 |
中 |
高 |
|
适用场景 |
配置、开关、Token |
业务数据、记录 |
多设备协同数据 |
5.2 选型决策树
遇到数据持久化需求时,可以按照下面的决策树来选型:
需要持久化数据
│
├── 数据需要跨设备同步?
│ ├── 是 → 分布式KV
│ └── 否 ↓
│
├── 数据结构复杂?需要查询/排序/筛选?
│ ├── 是 → RDB
│ └── 否 ↓
│
├── 数据量大?(超过几十KB)
│ ├── 是 → RDB
│ └── 否 ↓
│
└── Preferences ✅
简单总结:
-
配置类数据 → Preferences(最简单)
-
业务数据 → RDB(功能最强)
-
跨设备数据 → 分布式KV(鸿蒙特色)
六、常见坑与最佳实践
6.1 Preferences常见坑
|
问题 |
原因 |
解决方案 |
|---|---|---|
|
数据丢失 |
忘记调用 |
每次写入后都调用 |
|
内存溢出 |
存储了大图片或长文本 |
大数据用文件或RDB存储 |
|
并发问题 |
多线程同时读写 |
使用同一个Preferences实例 |
最佳实践:
// 好的做法:封装工具类
class PreferencesUtil {
private static store: preferences.Preferences | null = null;
static async init(context: Context) {
this.store = await preferences.getPreferences(context, 'appSettings');
}
static async set(key: string, value: preferences.ValueType) {
if (!this.store) return;
await this.store.put(key, value);
await this.store.flush(); // 立即持久化
}
static async get<T>(key: string, defaultValue: T): Promise<T> {
if (!this.store) return defaultValue;
return (await this.store.get(key, defaultValue)) as T;
}
}
6.2 RDB常见坑
|
问题 |
原因 |
解决方案 |
|---|---|---|
|
ResultSet泄漏 |
忘记关闭ResultSet |
使用完后调用 |
|
主线程卡顿 |
大量数据操作在主线程 |
异步操作,或用Worker线程 |
|
升级问题 |
表结构变更后崩溃 |
使用版本号+onUpgrade处理 |
最佳实践:
// 使用try-finally确保ResultSet关闭
async queryData(): Promise<TodoItem[]> {
const predicates = new relationalStore.RdbPredicates('todos');
const resultSet = await this.store.query(predicates, ['id', 'title', 'completed']);
try {
const result: TodoItem[] = [];
while (resultSet.goToNextRow()) {
// 处理数据...
}
return result;
} finally {
resultSet.close(); // 确保关闭
}
}
6.3 分布式KV常见坑
|
问题 |
原因 |
解决方案 |
|---|---|---|
|
同步不生效 |
未登录华为账号 |
引导用户登录 |
|
数据冲突 |
多设备同时修改同一key |
使用时间戳作为冲突解决策略 |
|
权限问题 |
未配置分布式权限 |
在module.json5中配置 |
module.json5权限配置:
{
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
七、总结
今天我们系统学习了鸿蒙的三大数据持久化方案:
-
Preferences:轻量级首选,适合配置、开关等简单数据,使用最简单
-
RDB:关系型数据库,适合业务数据、聊天记录等结构化数据,功能最强
-
分布式KV:鸿蒙特色,适合跨设备同步的协同数据
选型口诀:
-
配置开关用 Preferences
-
业务数据用 RDB
-
跨设备用 分布式KV
掌握了这三板斧,你的App数据再也不会"失忆"了!下一篇我们将学习文件管理与沙箱机制,敬请期待。
系列文章推荐
|
序号 |
文章标题 |
主题 |
|---|---|---|
|
01 |
系列介绍 |
|
|
02 |
入门基础 |
|
|
03 |
语言基础 |
|
|
04 |
UI基础 |
|
|
05 |
布局 |
|
|
06 |
状态管理 |
|
|
07 |
列表组件 |
|
|
08 |
路由导航 |
|
|
09 |
生命周期 |
|
|
10 |
组件化 |
|
|
11 |
动画 |
|
|
12 |
手势交互 |
|
|
13 |
网络 |
|
|
14 |
数据处理 |
|
|
15 |
应用模型 |
|
|
16 |
后台 |
|
|
17 |
权限 |
|
|
18 |
并发 |
|
|
19 |
性能 |
|
|
20 |
网络进阶 |
|
|
21 |
数据持久化三板斧(本文) |
数据存储 |
标签:#Preferences #RDB #分布式数据 #鸿蒙存储 #数据持久化 #ArkTS #HarmonyOS
💡 你觉得这篇文章有帮助吗? 欢迎点赞、收藏、评论,你的支持是我持续创作的动力!
更多推荐



所有评论(0)