鸿蒙Flutter JSON文件读写:Flutter在鸿蒙平台的数据持久化方案
·


概述
在移动应用开发中,数据持久化是一个重要的需求。将JSON数据保存到本地文件可以实现数据的离线存储和配置管理。Flutter提供了 dart:io 库来进行文件操作,结合 dart:convert 库可以方便地实现JSON文件的读写。
本章将详细介绍如何在Flutter和鸿蒙平台上实现JSON文件的读写操作,包括:
dart:io文件操作API- JSON序列化与文件IO的结合
- 异步文件操作的最佳实践
- 配置管理的完整实现
核心概念
文件IO基础
文件IO(Input/Output)是指与文件系统进行数据交换的操作。在Flutter中,dart:io 库提供了丰富的文件操作API:
- File类:用于操作单个文件
- Directory类:用于操作目录
- FileSystemEntity类:文件系统实体的基类
JSON文件的特点
JSON文件是一种轻量级的数据交换格式,具有以下特点:
- 可读性强:人类可读的文本格式
- 结构清晰:支持嵌套的对象和数组
- 跨平台:几乎所有编程语言都支持
- 易于解析:有成熟的解析库支持
异步文件操作
文件操作通常是耗时的IO操作,Flutter推荐使用异步方式进行文件操作,避免阻塞UI线程:
- 使用
async/await语法 - 返回
Future对象 - 使用
then()链式调用
实现方案
基本文件读写
写入JSON文件
import 'dart:convert';
import 'dart:io';
Future<void> saveJsonToFile(Map<String, dynamic> data, String filePath) async {
final File file = File(filePath);
final String jsonString = JsonEncoder.withIndent(' ').convert(data);
await file.writeAsString(jsonString);
}
读取JSON文件
import 'dart:convert';
import 'dart:io';
Future<Map<String, dynamic>?> loadJsonFromFile(String filePath) async {
final File file = File(filePath);
if (!await file.exists()) {
return null;
}
final String jsonString = await file.readAsString();
return json.decode(jsonString) as Map<String, dynamic>;
}
配置管理实现
创建配置管理类
import 'dart:convert';
import 'dart:io';
class ConfigManager {
static const String _configFileName = 'app_config.json';
static Future<String> get _configPath async {
final Directory appDir = Directory.current;
return '${appDir.path}/$_configFileName';
}
static Future<void> saveConfig(Map<String, dynamic> config) async {
final String path = await _configPath;
final File file = File(path);
final String jsonString = JsonEncoder.withIndent(' ').convert(config);
await file.writeAsString(jsonString);
}
static Future<Map<String, dynamic>?> loadConfig() async {
final String path = await _configPath;
final File file = File(path);
if (!await file.exists()) {
return null;
}
try {
final String jsonString = await file.readAsString();
return json.decode(jsonString) as Map<String, dynamic>;
} catch (e) {
print('配置文件读取失败: $e');
return null;
}
}
static Future<bool> deleteConfig() async {
final String path = await _configPath;
final File file = File(path);
if (await file.exists()) {
await file.delete();
return true;
}
return false;
}
static Future<bool> configExists() async {
final String path = await _configPath;
return File(path).exists();
}
}
文件操作方法汇总
| 方法 | 说明 | 返回值 |
|---|---|---|
File.exists() |
检查文件是否存在 | Future<bool> |
File.readAsString() |
读取文件内容 | Future<String> |
File.writeAsString() |
写入文件内容 | Future<void> |
File.delete() |
删除文件 | Future<void> |
Directory.create() |
创建目录 | Future<Directory> |
Directory.list() |
列出目录内容 | Stream<FileSystemEntity> |
实战案例
完整的配置管理应用
配置模型
class AppConfig {
String appName;
String version;
String theme;
String language;
Settings settings;
List<String> features;
String lastUpdated;
AppConfig({
required this.appName,
required this.version,
required this.theme,
required this.language,
required this.settings,
required this.features,
required this.lastUpdated,
});
Map<String, dynamic> toJson() {
return {
'appName': appName,
'version': version,
'theme': theme,
'language': language,
'settings': settings.toJson(),
'features': features,
'lastUpdated': lastUpdated,
};
}
factory AppConfig.fromJson(Map<String, dynamic> json) {
return AppConfig(
appName: json['appName'] as String,
version: json['version'] as String,
theme: json['theme'] as String,
language: json['language'] as String,
settings: Settings.fromJson(json['settings'] as Map<String, dynamic>),
features: (json['features'] as List).map((e) => e as String).toList(),
lastUpdated: json['lastUpdated'] as String,
);
}
}
class Settings {
bool notifications;
bool autoUpdate;
int cacheSize;
int timeout;
Settings({
required this.notifications,
required this.autoUpdate,
required this.cacheSize,
required this.timeout,
});
Map<String, dynamic> toJson() {
return {
'notifications': notifications,
'autoUpdate': autoUpdate,
'cacheSize': cacheSize,
'timeout': timeout,
};
}
factory Settings.fromJson(Map<String, dynamic> json) {
return Settings(
notifications: json['notifications'] as bool,
autoUpdate: json['autoUpdate'] as bool,
cacheSize: json['cacheSize'] as int,
timeout: json['timeout'] as int,
);
}
}
使用配置管理
void main() async {
// 创建配置
final AppConfig config = AppConfig(
appName: 'Flutter HarmonyOS Demo',
version: '1.0.0',
theme: 'dark',
language: 'zh_CN',
settings: Settings(
notifications: true,
autoUpdate: true,
cacheSize: 50,
timeout: 30,
),
features: ['weather', 'news', 'calendar'],
lastUpdated: DateTime.now().toIso8601String(),
);
// 保存配置
await ConfigManager.saveConfig(config.toJson());
print('配置保存成功');
// 检查配置是否存在
final bool exists = await ConfigManager.configExists();
print('配置文件存在: $exists');
// 加载配置
final Map<String, dynamic>? loaded = await ConfigManager.loadConfig();
if (loaded != null) {
final AppConfig loadedConfig = AppConfig.fromJson(loaded);
print('应用名称: ${loadedConfig.appName}');
print('主题: ${loadedConfig.theme}');
print('通知: ${loadedConfig.settings.notifications}');
}
// 删除配置
final bool deleted = await ConfigManager.deleteConfig();
print('配置删除成功: $deleted');
}
批量文件操作
import 'dart:convert';
import 'dart:io';
class JsonFileManager {
static Future<void> saveMultiple(
Map<String, Map<String, dynamic>> dataMap,
String directory
) async {
final Directory dir = Directory(directory);
if (!await dir.exists()) {
await dir.create(recursive: true);
}
for (final String key in dataMap.keys) {
final File file = File('$directory/$key.json');
final String jsonString = JsonEncoder.withIndent(' ').convert(dataMap[key]);
await file.writeAsString(jsonString);
}
}
static Future<Map<String, Map<String, dynamic>>> loadMultiple(
String directory,
List<String> fileNames
) async {
final Map<String, Map<String, dynamic>> result = {};
final Directory dir = Directory(directory);
if (!await dir.exists()) {
return result;
}
for (final String fileName in fileNames) {
final File file = File('$directory/$fileName.json');
if (await file.exists()) {
final String content = await file.readAsString();
result[fileName] = json.decode(content) as Map<String, dynamic>;
}
}
return result;
}
}
异步操作模式
使用async/await
Future<void> asyncFileOperation() async {
final File file = File('data.json');
// 写入文件
final Map<String, dynamic> data = {'name': '张三', 'age': 25};
await file.writeAsString(json.encode(data));
print('写入完成');
// 读取文件
final String content = await file.readAsString();
final Map<String, dynamic> loaded = json.decode(content) as Map<String, dynamic>;
print('读取完成: $loaded');
}
使用then链式调用
void thenFileOperation() {
final File file = File('data.json');
file.writeAsString(json.encode({'name': '李四'}))
.then((_) => file.readAsString())
.then((content) {
final Map<String, dynamic> loaded = json.decode(content) as Map<String, dynamic>;
print('数据: $loaded');
})
.catchError((error) => print('操作失败: $error'));
}
并行文件操作
Future<void> parallelOperations() async {
final List<File> files = [
File('file1.json'),
File('file2.json'),
File('file3.json'),
];
// 并行写入
final List<Future<void>> writeFutures = files.map((file) {
return file.writeAsString(json.encode({'id': files.indexOf(file)}));
}).toList();
await Future.wait(writeFutures);
print('所有文件写入完成');
// 并行读取
final List<Future<String>> readFutures = files.map((file) => file.readAsString()).toList();
final List<String> contents = await Future.wait(readFutures);
for (final String content in contents) {
print(content);
}
}
错误处理
文件不存在的处理
Future<Map<String, dynamic>?> safeLoadFile(String path) async {
final File file = File(path);
try {
if (!await file.exists()) {
print('文件不存在: $path');
return null;
}
final String content = await file.readAsString();
return json.decode(content) as Map<String, dynamic>;
} on FileSystemException catch (e) {
print('文件系统错误: $e');
return null;
} on FormatException catch (e) {
print('JSON格式错误: $e');
return null;
} catch (e) {
print('未知错误: $e');
return null;
}
}
权限检查
Future<bool> checkFilePermission(String path) async {
try {
final File file = File(path);
await file.exists();
return true;
} on FileSystemException catch (e) {
if (e.osError?.errorCode == 13) {
print('没有权限访问文件');
}
return false;
}
}
鸿蒙平台注意事项
文件路径管理
在鸿蒙平台上,不同应用有不同的文件存储路径:
import 'dart:io';
class HarmonyPathManager {
static Future<String> get appDocumentsPath async {
// 应用文档目录
final Directory appDir = Directory.current;
return appDir.path;
}
static Future<String> get cachePath async {
// 缓存目录
final Directory cacheDir = Directory.systemTemp;
return cacheDir.path;
}
static String getConfigPath(String fileName) {
// 构建配置文件路径
return '$appDocumentsPath/$fileName';
}
}
资源限制
在鸿蒙平台上需要注意:
- 存储空间限制:不同设备的可用空间不同
- 文件大小限制:单个文件不宜过大
- 权限管理:需要在manifest中声明文件访问权限
- 后台操作:避免在后台进行大量文件IO操作
性能优化
class OptimizedFileManager {
static Future<void> saveWithBackup(Map<String, dynamic> data, String path) async {
final File file = File(path);
final File backupFile = File('$path.bak');
// 先写入临时文件
final String jsonString = JsonEncoder.withIndent(' ').convert(data);
// 如果原文件存在,创建备份
if (await file.exists()) {
await file.copy(backupFile.path);
}
// 写入新内容
await file.writeAsString(jsonString);
// 删除备份
if (await backupFile.exists()) {
await backupFile.delete();
}
}
}
应用场景
场景一:用户配置存储
class UserSettingsManager {
static const String _settingsFile = 'user_settings.json';
static Future<void> saveUserSettings(Map<String, dynamic> settings) async {
final File file = File(_settingsFile);
await file.writeAsString(json.encode(settings));
}
static Future<Map<String, dynamic>> loadUserSettings() async {
final File file = File(_settingsFile);
if (!await file.exists()) {
return _getDefaultSettings();
}
final String content = await file.readAsString();
return json.decode(content) as Map<String, dynamic>;
}
static Map<String, dynamic> _getDefaultSettings() {
return {
'theme': 'light',
'language': 'zh_CN',
'notifications': true,
'autoUpdate': true,
};
}
}
场景二:离线数据缓存
class DataCacheManager {
static const String _cacheDir = 'cache';
static const int _maxCacheSize = 100;
static Future<void> cacheData(String key, Map<String, dynamic> data) async {
final Directory dir = Directory(_cacheDir);
if (!await dir.exists()) {
await dir.create();
}
final File file = File('$_cacheDir/$key.json');
await file.writeAsString(json.encode(data));
// 检查缓存大小
await _cleanupCache();
}
static Future<Map<String, dynamic>?> getCachedData(String key) async {
final File file = File('$_cacheDir/$key.json');
if (!await file.exists()) {
return null;
}
// 更新访问时间
await file.setLastModified(DateTime.now());
final String content = await file.readAsString();
return json.decode(content) as Map<String, dynamic>;
}
static Future<void> _cleanupCache() async {
final Directory dir = Directory(_cacheDir);
if (!await dir.exists()) return;
final List<FileSystemEntity> files = await dir.list().toList();
if (files.length > _maxCacheSize) {
// 按修改时间排序,删除最早的
files.sort((a, b) => a.statSync().modified.compareTo(b.statSync().modified));
for (int i = 0; i < files.length - _maxCacheSize; i++) {
await files[i].delete();
}
}
}
}
场景三:日志文件记录
class JsonLogger {
static const String _logFile = 'app_logs.json';
static List<Map<String, dynamic>> _logs = [];
static void log(String level, String message, {Map<String, dynamic>? extra}) {
final Map<String, dynamic> logEntry = {
'timestamp': DateTime.now().toIso8601String(),
'level': level,
'message': message,
'extra': extra,
};
_logs.add(logEntry);
// 定期写入文件
if (_logs.length >= 100) {
_flushLogs();
}
}
static Future<void> _flushLogs() async {
final File file = File(_logFile);
if (await file.exists()) {
final String content = await file.readAsString();
final List<dynamic> existingLogs = json.decode(content) as List<dynamic>;
_logs = [...existingLogs.cast<Map<String, dynamic>>(), ..._logs];
}
await file.writeAsString(JsonEncoder.withIndent(' ').convert(_logs));
_logs.clear();
}
static Future<void> flush() async {
if (_logs.isNotEmpty) {
await _flushLogs();
}
}
}
最佳实践
使用文件锁
在多线程环境下,需要使用文件锁避免并发写入:
class SafeFileWriter {
static Future<void> writeWithLock(File file, String content) async {
final RandomAccessFile raf = await file.open(mode: FileMode.write);
try {
await raf.lock();
await raf.writeString(content);
} finally {
await raf.unlock();
await raf.close();
}
}
}
增量更新
对于大型JSON文件,可以使用增量更新策略:
class IncrementalJsonUpdater {
static Future<void> updateField(
String filePath,
String fieldPath,
dynamic value
) async {
final File file = File(filePath);
if (!await file.exists()) {
return;
}
final String content = await file.readAsString();
final Map<String, dynamic> data = json.decode(content) as Map<String, dynamic>;
// 更新指定字段
_updateNestedField(data, fieldPath.split('.'), value);
await file.writeAsString(JsonEncoder.withIndent(' ').convert(data));
}
static void _updateNestedField(
Map<String, dynamic> data,
List<String> keys,
dynamic value
) {
if (keys.length == 1) {
data[keys[0]] = value;
return;
}
final String currentKey = keys[0];
if (!data.containsKey(currentKey)) {
data[currentKey] = <String, dynamic>{};
}
_updateNestedField(
data[currentKey] as Map<String, dynamic>,
keys.sublist(1),
value
);
}
}
版本控制
为配置文件添加版本号,便于升级处理:
class VersionedConfigManager {
static const String _configFile = 'config.json';
static const int _currentVersion = 2;
static Future<Map<String, dynamic>> loadConfig() async {
final File file = File(_configFile);
if (!await file.exists()) {
return _getDefaultConfig();
}
final String content = await file.readAsString();
final Map<String, dynamic> data = json.decode(content) as Map<String, dynamic>;
final int version = data['version'] as int? ?? 0;
if (version < _currentVersion) {
return _migrateConfig(data, version);
}
return data;
}
static Map<String, dynamic> _migrateConfig(Map<String, dynamic> oldConfig, int oldVersion) {
final Map<String, dynamic> newConfig = Map.from(oldConfig);
if (oldVersion < 2) {
// 添加新版本的字段
newConfig['newField'] = defaultValue;
}
newConfig['version'] = _currentVersion;
return newConfig;
}
static Map<String, dynamic> _getDefaultConfig() {
return {
'version': _currentVersion,
// ...其他默认配置
};
}
}
总结
JSON文件读写是Flutter应用开发中常用的数据持久化方式。通过 dart:io 和 dart:convert 库的结合使用,我们可以方便地实现数据的保存和读取。
关键要点:
- 使用
File类进行文件操作 - 使用
async/await进行异步操作 - 结合
JsonEncoder/JsonDecoder进行序列化 - 注意错误处理和权限管理
- 在鸿蒙平台上注意资源限制和性能优化
掌握这些技巧,可以为你的应用提供可靠的数据持久化方案。
更多推荐



所有评论(0)