Flutter中如何对比新旧HTTP数据实现智能缓存?
实现Post请求的智能缓存机制(先读缓存+后台更新+差异同步)
要实现类似Instagram的缓存效果,核心是先返回本地缓存快速响应UI,同时后台发起网络请求拉取最新数据,对比新旧数据后更新缓存并同步UI。由于你的请求是带参数的Post,需要先解决「不同请求参数对应不同缓存」的问题,再处理数据对比更新逻辑。
1. 生成唯一缓存Key(针对Post请求)
Post请求的缓存不能只依赖URL,必须把请求参数(requestBody)纳入缓存Key的生成逻辑,避免不同参数的请求共用缓存。可以将请求体JSON做哈希处理生成唯一Key:
import 'dart:convert'; import 'package:crypto/crypto.dart'; String generateCacheKey(String url, String requestBodyJson) { // 组合URL和请求体生成唯一标识 final combined = '$url:$requestBodyJson'; // 用MD5哈希生成短Key return md5.convert(utf8.encode(combined)).toString(); }
2. 修改fetchTotalDiaries方法,实现「先缓存后更新」逻辑
整合flutter_cache_manager,先读取缓存返回给UI,同时后台发起网络请求,对比数据后更新缓存:
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; // 自定义缓存管理器(也可直接使用DefaultCacheManager) final diaryCacheManager = CacheManager( Config( 'diary_cache', stalePeriod: const Duration(hours: 1), // 缓存过期时间可按需调整 ), ); Future<List> fetchTotalDiaries(UserProfile userProfile, int currentPage, {Function(List)? onDataUpdated}) async { try { Map<String, dynamic> requestBody = { 'user': userProfile.toJson(), 'currentPage': currentPage, 'offset': offset, }; String requestBodyJson = jsonEncode(requestBody); final cacheKey = generateCacheKey(totalDiarygroupFunctionsUri.toString(), requestBodyJson); // 1. 优先读取本地缓存,快速返回给UI final cachedFile = await diaryCacheManager.getFileFromCache(cacheKey); if (cachedFile != null) { final cachedData = jsonDecode(await cachedFile.file.readAsString()); // 后台异步发起网络请求更新数据 _fetchAndUpdateCache(requestBodyJson, cacheKey, onDataUpdated); return cachedData; } // 2. 无缓存时直接发起网络请求 return await _fetchAndUpdateCache(requestBodyJson, cacheKey, onDataUpdated); } catch (e) { print("fetchTotalDiaries --> $e"); return []; } } // 封装网络请求+缓存更新逻辑 Future<List> _fetchAndUpdateCache(String requestBodyJson, String cacheKey, Function(List)? onDataUpdated) async { try { final response = await http.post( totalDiarygroupFunctionsUri, body: requestBodyJson, headers: headers, ); if (response.statusCode == 200) { final data = jsonDecode(utf8.decode(response.bodyBytes))["data"]; final newDataJson = jsonEncode(data); // 读取旧缓存数据,判断是否需要更新 final cachedFile = await diaryCacheManager.getFileFromCache(cacheKey); bool needUpdate = true; if (cachedFile != null) { final oldDataJson = await cachedFile.file.readAsString(); // 简单对比:直接比较JSON字符串(复杂数据建议用深度对比或时间戳) needUpdate = oldDataJson != newDataJson; } // 数据有变化时更新缓存并通知UI if (needUpdate) { await diaryCacheManager.putFile( cacheKey, utf8.encode(newDataJson), key: cacheKey, ); onDataUpdated?.call(data); } return data; } } catch (e) { print("_fetchAndUpdateCache --> $e"); // 网络请求失败时,尝试返回缓存数据 final cachedFile = await diaryCacheManager.getFileFromCache(cacheKey); if (cachedFile != null) { return jsonDecode(await cachedFile.file.readAsString()); } } return []; }
3. 优化数据对比逻辑(可选但推荐)
上面用JSON字符串全量对比适合简单数据,若接口返回updated_at时间戳或版本号,可改用更高效的对比方式:
- 让接口返回格式包含更新时间:
{"data": [...], "updated_at": "2024-05-20T12:00:00Z"} - 对比缓存数据和新数据的
updated_at,判断是否需要更新,避免全量字符串对比的性能消耗
4. UI层处理数据更新
调用fetchTotalDiaries时传入onDataUpdated回调,在回调中更新UI状态:
// 在Widget的状态类中 void _loadDiaries() async { // 先显示缓存数据 final cachedData = await fetchTotalDiaries(userProfile, currentPage, onDataUpdated: (newData) { // 数据更新后刷新UI setState(() { _diaries = newData; }); }); setState(() { _diaries = cachedData; }); }
内容的提问来源于stack exchange,提问作者Hyejung
相关产品推荐
相关产品推荐

