You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:54:58