MST

星途 面试题库

面试题:Flutter中SharedPreferences与跨平台数据一致性及性能调优

在Flutter跨平台应用开发中,使用SharedPreferences存储数据时,如何保证不同平台(如iOS和Android)之间数据的一致性?当面对高并发读写操作时,如何优化SharedPreferences的性能以避免数据丢失或读写冲突?请从底层原理和具体实现方面详细阐述。
14.5万 热度难度
前端开发Flutter

知识考点

AI 面试

面试题答案

一键面试

保证不同平台数据一致性

  1. 底层原理
    • SharedPreferences在Android上基于XML文件存储数据,在iOS上是基于NSUserDefaults存储数据。虽然存储机制不同,但Flutter的SharedPreferences插件通过统一的接口抽象,为开发者提供了一致的操作方式。
    • 关键在于确保在不同平台上对数据的写入和读取遵循相同的逻辑和格式。例如,在数据类型转换上要保持一致,如将intdoubleboolString等类型正确转换和存储。
  2. 具体实现
    • 数据类型一致性
      • 在写入数据时,确保在不同平台上以相同的数据类型存储。比如,将一个int值写入SharedPreferences,无论是在Android还是iOS,都应该以int类型正确存储。在Flutter中,SharedPreferences插件会处理这些类型转换,但开发者要注意传递正确的数据类型。例如:
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 10);
  • 版本控制
    • 可以在应用中引入版本号机制。每次对存储数据的结构或格式进行更改时,更新版本号。在读取数据时,先检查版本号,根据版本号进行相应的数据迁移操作。例如:
final prefs = await SharedPreferences.getInstance();
int? version = prefs.getInt('dataVersion');
if (version == null) {
  // 初始化版本号
  await prefs.setInt('dataVersion', 1);
  // 初始化数据
} else if (version == 1) {
  // 执行版本1的数据迁移逻辑
  await prefs.setInt('dataVersion', 2);
}

优化高并发读写性能

  1. 底层原理
    • AndroidSharedPreferences的写入操作是异步的,通过commit()apply()方法。commit()方法是同步的,会阻塞当前线程直到写入完成;apply()方法是异步的,会将写入操作提交到一个单线程的DiskWriteThread队列中执行。读取操作是同步的,直接从内存中的SharedPreferencesImpl对象获取数据,如果内存中没有,则从磁盘加载。
    • iOSNSUserDefaults的读写操作相对简单,读取直接从内存缓存中获取,写入则是异步地将更改写入磁盘。但在高并发场景下,也可能出现数据冲突问题。
  2. 具体实现
    • 读写队列
      • 可以创建一个单线程的队列来处理SharedPreferences的读写操作。使用compute函数在一个单独的隔离计算线程中执行读写任务,避免在主线程中出现高并发冲突。例如:
import 'dart:async';
import 'dart:isolate';

class SharedPrefsQueue {
  final ReceivePort _receivePort = ReceivePort();
  static const _channelName = 'shared_prefs_queue';

  SharedPrefsQueue() {
    Isolate.spawn(_worker, _receivePort.sendPort);
  }

  static Future<void> _worker(SendPort sendPort) async {
    final ReceivePort receivePort = ReceivePort();
    sendPort.send(receivePort.sendPort);
    await for (final message in receivePort) {
      if (message is Function) {
        message();
      }
    }
  }

  Future<void> performOperation(Future<void> Function() operation) async {
    final sendPort = await _receivePort.first;
    sendPort.send(operation);
  }
}
  • 批量操作
    • 避免频繁的小读写操作,将多个读写操作合并为一次批量操作。例如,将多个数据的写入操作合并:
final prefs = await SharedPreferences.getInstance();
final batch = prefs.edit();
batch.setInt('key1', 1);
batch.setString('key2', 'value2');
await batch.apply();
  • 缓存机制
    • 在内存中维护一个缓存,对于频繁读取的数据,先从缓存中获取。只有在缓存中不存在或者数据过期时,才从SharedPreferences中读取。在写入数据时,同时更新缓存,确保缓存与SharedPreferences的数据一致性。例如:
class SharedPrefsCache {
  final Map<String, dynamic> _cache = {};
  final SharedPreferences _prefs;

  SharedPrefsCache(this._prefs);

  Future<dynamic> getValue(String key) async {
    if (_cache.containsKey(key)) {
      return _cache[key];
    }
    final value = await _prefs.get(key);
    _cache[key] = value;
    return value;
  }

  Future<void> setValue(String key, dynamic value) async {
    await _prefs.setString(key, value.toString());
    _cache[key] = value;
  }
}