Flutter for OpenHarmony 实战:数据持久化方案深度解析
本文深入解析Flutter for OpenHarmony平台的数据持久化方案,以记账助手应用为例,重点介绍了SharedPreferences和JSON序列化技术。文章首先概述移动应用数据存储需求及Flutter常用方案选择标准,详细讲解SharedPreferences的基本使用方法和异步操作最佳实践。随后深入探讨JSON序列化技术,包括复杂对象和列表数据的序列化与反序列化实现。通过本文,开发
Flutter for OpenHarmony 实战:数据持久化方案深度解析
文章目录
摘要
数据持久化是移动应用开发的核心技术之一。本文以记账助手应用为例,深入讲解Flutter for OpenHarmony平台上的数据持久化方案,包括SharedPreferences的使用方法、JSON序列化技术、数据模型设计最佳实践等。通过本文学习,读者将掌握Flutter应用中数据存储的完整实现方案,了解不同存储场景的最佳选择。
一、移动应用数据存储概述
1.1 为什么需要数据持久化
移动应用中的数据持久化是指将数据保存到非易失性存储介质中,确保应用关闭后数据不会丢失。
应用场景
- 用户偏好设置
- 业务数据存储
- 缓存临时数据
- 离线数据支持
持久化的价值
- 提升用户体验
- 支持离线使用
- 保障数据安全
- 实现跨会话状态保持
1.2 Flutter平台常用存储方案
| 存储方案 | 适用场景 | 数据类型 | 复杂度 |
|---|---|---|---|
| SharedPreferences | 简单键值对存储 | 基本类型 | 低 |
| SQLite | 结构化数据存储 | 复杂关系数据 | 中 |
| 文件存储 | 大文件存储 | 文件、图片 | 中 |
| Hive | NoSQL数据库 | 复杂对象 | 中 |
1.3 选择合适的存储方案
选择SharedPreferences的场景
- 数据量小(通常少于1MB)
- 数据结构简单
- 需要快速读写
- 主要是键值对数据
选择SQLite的场景
- 数据量大
- 需要复杂查询
- 数据关系复杂
- 需要事务支持
二、SharedPreferences详解
2.1 SharedPreferences简介
SharedPreferences是Flutter平台上常用的轻量级存储方案,通过键值对的方式存储数据。
核心特性
- 异步读写操作
- 支持基本数据类型
- 线程安全
- 自动持久化
支持的数据类型
- String(字符串)
- int(整数)
- double(浮点数)
- bool(布尔值)
- StringList(字符串列表)
2.2 添加依赖
在pubspec.yaml中添加依赖:
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.2.2
安装依赖:
flutter pub get
2.3 基本使用方法
获取实例
final prefs = await SharedPreferences.getInstance();
写入数据
// 保存字符串
await prefs.setString('username', '张三');
// 保存整数
await prefs.setInt('age', 25);
// 保存布尔值
await prefs.setBool('isLoggedIn', true);
// 保存浮点数
await prefs.setDouble('balance', 1234.56);
// 保存字符串列表
await prefs.setStringList('tags', ['工作', '生活', '学习']);
读取数据
// 读取字符串(带默认值)
String username = prefs.getString('username') ?? '匿名用户';
// 读取整数(带默认值)
int age = prefs.getInt('age') ?? 0;
// 读取布尔值(带默认值)
bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
// 读取浮点数(带默认值)
double balance = prefs.getDouble('balance') ?? 0.0;
// 读取字符串列表(带默认值)
List<String> tags = prefs.getStringList('tags') ?? [];
删除数据
// 删除单个键
await prefs.remove('username');
// 清空所有数据
await prefs.clear();
检查键是否存在
bool hasKey = prefs.containsKey('username');
2.4 异步操作最佳实践
SharedPreferences的所有写操作都是异步的,需要正确处理:
// 好的做法:使用await等待完成
Future<void> saveUserData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', '张三');
await prefs.setInt('age', 25);
// 保存完成后的操作
print('数据保存成功');
}
// 不好的做法:不等待完成
void saveUserDataBad() {
SharedPreferences.getInstance().then((prefs) {
prefs.setString('username', '张三');
// 无法确定何时保存完成
});
}
三、JSON序列化技术
3.1 JSON简介
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。
基本数据类型
- 对象(花括号包围)
- 数组(方括号包围)
- 字符串(双引号包围)
- 数字(整数或浮点数)
- 布尔值(true/false)
- null
3.2 Dart中的JSON处理
Dart提供了dart:convert库来处理JSON数据:
import 'dart:convert';
JSON编码(序列化)
// 将对象转换为JSON字符串
Map<String, dynamic> data = {
'name': '张三',
'age': 25,
'isActive': true,
};
String jsonString = jsonEncode(data);
print(jsonString); // {"name":"张三","age":25,"isActive":true}
JSON解码(反序列化)
// 将JSON字符串转换为对象
String jsonString = '{"name":"张三","age":25,"isActive":true}';
Map<String, dynamic> data = jsonDecode(jsonString);
print(data['name']); // 张三
print(data['age']); // 25
3.3 复杂对象的序列化
对于复杂的业务对象,需要手动实现序列化方法:
class Transaction {
final String id;
final String title;
final double amount;
final DateTime date;
Transaction({
required this.id,
required this.title,
required this.amount,
required this.date,
});
// 序列化方法
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'amount': amount,
'date': date.millisecondsSinceEpoch, // DateTime转时间戳
};
}
// 反序列化工厂方法
factory Transaction.fromJson(Map<String, dynamic> json) {
return Transaction(
id: json['id'] as String,
title: json['title'] as String,
amount: json['amount'] as double,
date: DateTime.fromMillisecondsSinceEpoch(json['date'] as int),
);
}
}
// 使用示例
Transaction transaction = Transaction(
id: '1',
title: '午餐',
amount: 35.50,
date: DateTime.now(),
);
// 序列化
String jsonString = jsonEncode(transaction.toJson());
// 反序列化
Map<String, dynamic> json = jsonDecode(jsonString);
Transaction restored = Transaction.fromJson(json);
3.4 列表数据的序列化
// 对象列表
List<Transaction> transactions = [
Transaction(id: '1', title: '午餐', amount: 35.50, date: DateTime.now()),
Transaction(id: '2', title: '交通', amount: 5.00, date: DateTime.now()),
];
// 序列化为JSON字符串列表
List<String> jsonList = transactions.map((t) => jsonEncode(t.toJson())).toList();
// 保存到SharedPreferences
await prefs.setStringList('transactions', jsonList);
// 从SharedPreferences读取
List<String>? savedJsonList = prefs.getStringList('transactions');
// 反序列化为对象列表
List<Transaction> restoredTransactions = savedJsonList?.map((json) {
return Transaction.fromJson(jsonDecode(json) as Map<String, dynamic>);
}).toList() ?? [];
四、数据模型设计最佳实践
4.1 不可变对象设计
使用final关键字确保对象不可变:
class Transaction {
final String id; // 不可变ID
final String title; // 不可变标题
final double amount; // 不可变金额
final DateTime date; // 不可变日期
Transaction({
required this.id,
required this.title,
required this.amount,
required this.date,
});
}
不可变对象的优势
- 线程安全
- 防止意外修改
- 便于状态管理
- 支持值比较
4.2 命名构造函数
提供便捷的构造方法:
class Transaction {
final String id;
final String title;
final double amount;
final DateTime date;
// 默认构造函数
Transaction({
required this.id,
required this.title,
required this.amount,
required this.date,
});
// 命名构造函数:创建当前时间的新记录
Transaction.create({
required this.title,
required this.amount,
}) : id = DateTime.now().millisecondsSinceEpoch.toString(),
date = DateTime.now();
// 命名构造函数:从JSON创建
factory Transaction.fromJson(Map<String, dynamic> json) {
return Transaction(
id: json['id'] as String,
title: json['title'] as String,
amount: json['amount'] as double,
date: DateTime.fromMillisecondsSinceEpoch(json['date'] as int),
);
}
}
4.3 类型安全处理
使用as关键字进行类型转换:
factory Transaction.fromJson(Map<String, dynamic> json) {
return Transaction(
id: json['id'] as String, // 明确类型转换
title: json['title'] as String,
amount: (json['amount'] as num).toDouble(), // 处理int到double的转换
date: DateTime.fromMillisecondsSinceEpoch(json['date'] as int),
);
}
使用空值合并运算符提供默认值
String id = json['id'] as String? ?? '';
double amount = (json['amount'] as num?)?.toDouble() ?? 0.0;
4.4 数据验证
在fromJson中添加数据验证:
factory Transaction.fromJson(Map<String, dynamic> json) {
// 验证必需字段
if (!json.containsKey('id') || json['id'] == null) {
throw ArgumentError('缺少必需字段: id');
}
// 验证数据类型
final amount = json['amount'];
if (amount is! num) {
throw ArgumentError('amount必须是数字类型');
}
return Transaction(
id: json['id'] as String,
title: json['title'] as String? ?? '',
amount: amount.toDouble(),
date: json['date'] != null
? DateTime.fromMillisecondsSinceEpoch(json['date'] as int)
: DateTime.now(),
);
}
五、实战:实现记账应用数据持久化
5.1 数据加载实现
Future<void> _loadData() async {
try {
// 获取SharedPreferences实例
final prefs = await SharedPreferences.getInstance();
// 读取保存的JSON字符串列表
final transactionsJson = prefs.getStringList('transactions') ?? [];
// 反序列化为Transaction对象列表
setState(() {
_transactions = transactionsJson.map((json) {
return Transaction.fromJson(
jsonDecode(json) as Map<String, dynamic>
);
}).toList();
// 重新计算统计
_calculateTotals();
});
} catch (e) {
// 错误处理
print('加载数据失败: $e');
setState(() {
_transactions = [];
_calculateTotals();
});
}
}
加载流程解析

5.2 数据保存实现
Future<void> _saveData() async {
try {
// 获取SharedPreferences实例
final prefs = await SharedPreferences.getInstance();
// 将Transaction列表序列化为JSON字符串列表
final transactionsJson = _transactions.map((t) {
return jsonEncode(t.toJson());
}).toList();
// 保存到本地
await prefs.setStringList('transactions', transactionsJson);
print('数据保存成功,共${_transactions.length}条记录');
} catch (e) {
print('保存数据失败: $e');
}
}
保存流程解析

5.3 添加记录的数据保存
void _addTransaction(Transaction transaction) {
setState(() {
// 添加到内存列表
_transactions.add(transaction);
// 重新计算统计
_calculateTotals();
});
// 持久化到本地
_saveData();
}
5.4 删除记录的数据更新
void _deleteTransaction(String id) {
setState(() {
// 从内存列表中移除
_transactions.removeWhere((t) => t.id == id);
// 重新计算统计
_calculateTotals();
});
// 更新持久化数据
_saveData();
}
5.5 初始化加载
在initState中加载数据:
void initState() {
super.initState();
_loadData();
}
六、性能优化与错误处理
6.1 性能优化策略
批量操作
// 不好的做法:每次添加都保存
void addTransactionBad(Transaction transaction) {
_transactions.add(transaction);
_saveData(); // 频繁的IO操作
}
// 好的做法:批量操作后统一保存
void addTransactionsGood(List<Transaction> transactions) {
setState(() {
_transactions.addAll(transactions);
_calculateTotals();
});
_saveData(); // 只保存一次
}
避免过度序列化
// 不好的做法:每次都重新序列化整个列表
Widget build(BuildContext context) {
final jsonList = _transactions.map((t) => jsonEncode(t.toJson())).toList();
return Text('${jsonList.length}');
}
// 好的做法:使用已加载的数据
Widget build(BuildContext context) {
return Text('共${_transactions.length}条记录');
}
6.2 错误处理机制
完整的错误处理
Future<void> _loadData() async {
try {
final prefs = await SharedPreferences.getInstance();
final transactionsJson = prefs.getStringList('transactions') ?? [];
setState(() {
_transactions = transactionsJson.map((json) {
try {
return Transaction.fromJson(
jsonDecode(json) as Map<String, dynamic>
);
} catch (e) {
// 跳过损坏的数据
print('解析失败,跳过该记录: $e');
return null;
}
}).whereType<Transaction>().toList();
_calculateTotals();
});
} catch (e) {
print('加载数据失败: $e');
setState(() {
_transactions = [];
_calculateTotals();
});
}
}
6.3 数据版本管理
当数据结构变化时,需要处理版本兼容:
class Transaction {
final String id;
final String title;
final double amount;
final DateTime date;
final String? note; // 新增字段
Transaction({
required this.id,
required this.title,
required this.amount,
required this.date,
this.note, // 可选字段
});
factory Transaction.fromJson(Map<String, dynamic> json) {
return Transaction(
id: json['id'] as String,
title: json['title'] as String,
amount: (json['amount'] as num).toDouble(),
date: DateTime.fromMillisecondsSinceEpoch(json['date'] as int),
// 处理新字段的兼容性
note: json['note'] as String?, // 新版本有此字段
);
}
}
七、总结
本文深入讲解了Flutter for OpenHarmony平台上的数据持久化方案,主要内容包括:
- SharedPreferences:掌握轻量级键值对存储的使用方法
- JSON序列化:理解dart:convert的使用技巧
- 数据模型设计:学会设计类型安全的数据模型类
- 实战应用:实现完整的记账应用数据持久化
- 性能优化:了解批量操作和错误处理策略
数据持久化是移动应用的基础能力,掌握这些技术可以让你的应用具备真正的实用价值。通过不断实践,读者可以根据应用需求选择合适的存储方案,构建出功能完善、性能优异的应用。
欢迎加入开源鸿蒙跨平台社区: 开源鸿蒙跨平台开发者社区
更多推荐



所有评论(0)