MST

星途 面试题库

面试题:Flutter SharedPreferences批量操作下的数据一致性与持久化

当在Flutter中进行SharedPreferences的批量操作时,尤其是在多线程或异步环境下,如何保证数据的一致性和持久化?例如,在批量写入过程中突然发生设备断电或应用崩溃,如何确保已写入的数据完整且正确,未写入的数据不会造成数据不一致的情况?请深入分析并给出具体的解决方案和相关代码实现。
47.7万 热度难度
前端开发Flutter

知识考点

AI 面试

面试题答案

一键面试

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方法在批量写入数据时,记录每个成功写入的键到操作日志中,若某个操作失败则回滚已成功的操作。同时,成功的操作日志会被持久化到文件中,应用启动时会尝试加载日志以恢复之前的操作状态。