面试题答案
一键面试1. 分析问题
在Flutter中使用SharedPreferences
进行批量操作且在多线程或异步环境下,要保证数据一致性和持久化面临以下挑战:
- 原子性:批量操作需要作为一个整体执行,要么全部成功,要么全部失败,以避免部分数据写入成功,部分失败导致的数据不一致。
- 持久化:确保即使在设备断电或应用崩溃的情况下,已成功写入的数据能够完整保存。
2. 解决方案
- 使用事务:虽然
SharedPreferences
本身没有直接提供事务支持,但可以通过记录操作日志,在每次操作前和操作后更新日志状态,然后在批量操作完成后检查日志,确保所有操作都成功。如果有失败的操作,可以根据日志进行回滚。 - 异步操作的同步化:在多线程或异步环境下,使用锁机制(如
Lock
类)来确保同一时间只有一个操作在进行,避免并发操作导致的数据不一致。
3. 代码实现
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
class SharedPreferencesBatch {
SharedPreferences? prefs;
List<String> operationLog = [];
final lock = Lock();
Future<void> init() async {
prefs = await SharedPreferences.getInstance();
}
Future<void> batchWrite(Map<String, dynamic> data) async {
await lock.synchronized(() async {
try {
// 开始记录操作日志
operationLog = [];
for (var entry in data.entries) {
var success = false;
if (entry.value is String) {
success = await prefs!.setString(entry.key, entry.value);
} else if (entry.value is int) {
success = await prefs!.setInt(entry.key, entry.value);
} else if (entry.value is double) {
success = await prefs!.setDouble(entry.key, entry.value);
} else if (entry.value is bool) {
success = await prefs!.setBool(entry.key, entry.value);
} else if (entry.value is List<String>) {
success = await prefs!.setStringList(entry.key, entry.value);
}
if (success) {
operationLog.add(entry.key);
} else {
// 如果某个操作失败,回滚已成功的操作
for (var key in operationLog) {
prefs!.remove(key);
}
operationLog = [];
throw Exception('Batch write failed for key: ${entry.key}');
}
}
// 批量操作成功,保存日志到文件(持久化记录)
await _saveOperationLogToFile();
} catch (e) {
rethrow;
}
});
}
Future<void> _saveOperationLogToFile() async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/operation_log.txt');
await file.writeAsString(jsonEncode(operationLog));
}
Future<void> _loadOperationLogFromFile() async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/operation_log.txt');
if (await file.exists()) {
final logString = await file.readAsString();
operationLog = List<String>.from(jsonDecode(logString));
}
} catch (e) {
// 忽略加载失败的情况
}
}
}
使用示例:
void main() async {
var batch = SharedPreferencesBatch();
await batch.init();
await batch._loadOperationLogFromFile();
try {
await batch.batchWrite({
'key1': 'value1',
'key2': 2,
'key3': true
});
print('Batch write successful');
} catch (e) {
print('Batch write failed: $e');
}
}
上述代码实现了一个SharedPreferencesBatch
类,通过操作日志和锁机制来确保在批量操作SharedPreferences
时的数据一致性和持久化。batchWrite
方法在批量写入数据时,记录每个成功写入的键到操作日志中,若某个操作失败则回滚已成功的操作。同时,成功的操作日志会被持久化到文件中,应用启动时会尝试加载日志以恢复之前的操作状态。