Loading... key-value 数据库在很多时候都是用来作为缓存使用的。而大部分 key-value 数据库都能配置缓存过期的时,但是在 Flutter 中常用的 `shared_preferences` 却没有相应的缓存过期策略,不过我们可以自己手动实现。 ## 封装 shared_preferences `shared_preferences` 本身使用起来已经非常方便了,所以只要进行简单的封装就可以,这里使用泛型的方式。 ```dart import 'package:shared_preferences/shared_preferences.dart'; class PrefUtil { late final SharedPreferencesWithCache _prefs; Future<void> initPref() async { _prefs = await SharedPreferencesWithCache.create( cacheOptions: const SharedPreferencesWithCacheOptions(allowList: <String>{ })); } Future<void> reset() async { await _prefs.clear(); } Future<void> setValue<T>(String key, T value) async { if (T == int) { await _prefs.setInt(key, value as int); } else if (T == bool) { await _prefs.setBool(key, value as bool); } else if (T == double) { await _prefs.setDouble(key, value as double); } else if (T == String) { await _prefs.setString(key, value as String); } else if (T == List<String>) { await _prefs.setStringList(key, value as List<String>); } else { throw ArgumentError('Unsupported type: $T'); } } T? getValue<T>(String key) { if (T == int) { return _prefs.getInt(key) as T?; } else if (T == bool) { return _prefs.getBool(key) as T?; } else if (T == double) { return _prefs.getDouble(key) as T?; } else if (T == String) { return _prefs.getString(key) as T?; } else if (T == List<String>) { return _prefs.getStringList(key) as T?; } else { throw ArgumentError('Unsupported type: $T'); } } Future<void> removeValue(String key) async { await _prefs.remove(key); } } ``` 需要注意的时,在 `shared_preferences` 的 2.3.0 版本之后,由于底层实现方式的改变,原有的 `SharedPreferences` 类型被替换为了 `SharedPreferencesAsync` 和 `SharedPreferencesWithCache` ,简单的说就是为了安全考虑,分为了异步调用,和同步调用,当使用 `SharedPreferencesAsync` 时,用于取值的 get 方法返回值是一个 `Future` ,也就是必须异步调用。而 `SharedPreferencesWithCache` 则仍可以使用同步调用的方式,和名字一样,原理是通过缓存的方式来实现的,出于安全考虑,现在要使用同步调用的话,必须要预设好 `allowList` ,也就是将来要存储数据的 `key` ,对不在列表中的 `key` 将会抛出异常。也就是说,如果你不知道将来要存储内容的 `key` 值是多少,就要使用 `SharedPreferencesAsync` 进行异步调用。 ## 缓存控制类 其实实现的原理也非常简单,只要在存储原有数据的同时,记录下存储时的时间戳,下次取数据的时候,对比一下时间戳的差值,如果过期了,就可以进行进一步的操作。因为 `shared_perferences` 可以直接存储 `List<String>` ,所以我们直接将要存储的值放入一个列表中即可。 ```dart import 'dart:async'; import 'package:mood_diary/utils/utils.dart'; class CacheUtil { Future<List<String>?> getCacheList(String key, Future<List<String>?> Function() fetchData, {int maxAgeMillis = 900000}) async { var cachedData = Utils().prefUtil.getValue<List<String>>(key); // 检查缓存是否有效,如果无效则更新缓存 if (cachedData == null || _isCacheExpired(cachedData, maxAgeMillis)) { await _updateCacheList(key, fetchData); cachedData = Utils().prefUtil.getValue<List<String>>(key); // 获取更新后的缓存数据 } return cachedData; } bool _isCacheExpired(List<String> cachedData, int maxAgeMillis) { if (cachedData.length < 2) { return true; // 缓存数据格式不正确,视为过期 } int timestamp = int.parse(cachedData.last); return DateTime.now().millisecondsSinceEpoch - timestamp >= maxAgeMillis; } Future<void> _updateCacheList(String key, Future<List<String>?> Function() fetchData) async { var newData = await fetchData(); if (newData != null) { await Utils() .prefUtil .setValue<List<String>>(key, newData..add(DateTime.now().millisecondsSinceEpoch.toString())); } } } ``` 使用也非常简单,要取值时只需要调用 `getCacheList` 方法即可,`fetchData` 是一个函数,用于更新数据,这样就可以实现数据的自动更新。 © 允许规范转载 打赏 赞赏作者 微信 赞 2