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

如何在Flutter中实现API响应月级缓存并每月初自动刷新?

实现Flutter API调用的月级缓存机制

针对你的需求,我们可以通过缓存月份标识结合SQFlite(或Hive)实现月级缓存,核心逻辑是:用YYYY-MM格式字符串标记缓存所属月份,每次请求先判断本地缓存是否属于当前月份,是则复用,否则重新请求并更新缓存。

方案一:基于SQFlite的实现

1. 调整数据模型与数据库表结构

给PrayerTimes模型添加缓存月份字段,同时更新SQFlite表结构:

class PrayerTimes {
  final String date;
  final String fajr;
  // 其他原有字段...
  final String cacheMonth; // 格式:YYYY-MM

  PrayerTimes({
    required this.date,
    required this.fajr,
    // 其他字段参数...
    required this.cacheMonth,
  });

  // 从JSON转换时自动设置当前月份为缓存标识
  factory PrayerTimes.fromJson(Map<String, dynamic> json) {
    return PrayerTimes(
      date: json['date'],
      fajr: json['fajr'],
      // 其他字段赋值...
      cacheMonth: DateFormat('yyyy-MM').format(DateTime.now()),
    );
  }

  // 转换为SQFlite存储的Map
  Map<String, dynamic> toMap() {
    return {
      'date': date,
      'fajr': fajr,
      // 其他字段...
      'cache_month': cacheMonth,
    };
  }
}

更新SQFlite数据库操作类,增加缓存有效性检查方法:

class LocalDatabase {
  static Database? _db;

  static Future<Database> get db async {
    if (_db != null) return _db!;
    _db = await _initDB();
    return _db!;
  }

  static Future<Database> _initDB() async {
    final path = await getDatabasesPath();
    return openDatabase(
      join(path, 'prayer_times.db'),
      onCreate: (db, version) {
        return db.execute('''
          CREATE TABLE prayer_times(
            date TEXT PRIMARY KEY,
            fajr TEXT,
            -- 其他字段...
            cache_month TEXT NOT NULL
          )
        ''');
      },
      version: 1,
    );
  }

  // 插入或更新缓存(覆盖同日期数据)
  static Future<void> upsertPrayerTimes(PrayerTimes data) async {
    final db = await LocalDatabase.db;
    await db.insert(
      'prayer_times',
      data.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  // 查询当前月份的当日缓存
  static Future<PrayerTimes?> getValidCachedData(String currentDate) async {
    final db = await LocalDatabase.db;
    final currentMonth = DateFormat('yyyy-MM').format(DateTime.now());
    final result = await db.query(
      'prayer_times',
      where: 'date = ? AND cache_month = ?',
      whereArgs: [currentDate, currentMonth],
    );
    return result.isNotEmpty ? PrayerTimes.fromJson(result.first) : null;
  }
}

2. 修改API调用逻辑,加入缓存判断

重写API方法,优先读取有效缓存,无效则请求并更新:

class PrayerTimesApi {
  static Future<PrayerTimes> getPrayerTimes() async {
    final currentDate = DateFormat('EEEE dd MMMM yyyy').format(DateTime.now());
    
    // 第一步:检查有效缓存
    final cachedData = await LocalDatabase.getValidCachedData(currentDate);
    if (cachedData != null) {
      return cachedData;
    }

    // 第二步:缓存无效,请求API
    const url = 'example.com';
    final response = await http.post(
      Uri.parse(url),
      headers: {'Content-Type': 'application/json; charset=UTF-8'},
    );

    if (response.statusCode == 200) {
      final responseData = jsonDecode(response.body);
      final prayerTimesList = responseData['prayerTimes'] as List;
      
      // 查找当日数据
      final currentDayData = prayerTimesList.firstWhere(
        (item) => item['date'] == currentDate,
        orElse: () => throw '当前日期$currentDate的祈祷时间不可用',
      );

      final prayerTimes = PrayerTimes.fromJson(currentDayData);
      await LocalDatabase.upsertPrayerTimes(prayerTimes);
      return prayerTimes;
    } else {
      throw '请求失败,状态码:${response.statusCode}';
    }
  }
}

方案二:基于Hive的轻量实现(推荐)

如果不需要复杂SQL查询,Hive作为NoSQL轻量存储更高效,适合直接缓存整月数据:

1. 初始化Hive与注册Adapter

void initHive() async {
  await Hive.initFlutter();
  Hive.registerAdapter(PrayerTimesAdapter()); // 需要通过build_runner生成Adapter
  await Hive.openBox<List<PrayerTimes>>('prayer_times');
}

2. 实现缓存逻辑

class PrayerTimesApi {
  static Future<PrayerTimes> getPrayerTimes() async {
    final currentDate = DateFormat('EEEE dd MMMM yyyy').format(DateTime.now());
    final currentMonth = DateFormat('yyyy-MM').format(DateTime.now());
    final box = Hive.box<List<PrayerTimes>>('prayer_times');

    // 检查当月缓存
    final cachedMonthData = box.get(currentMonth);
    if (cachedMonthData != null) {
      return cachedMonthData.firstWhere(
        (pt) => pt.date == currentDate,
        orElse: () => throw '当前日期$currentDate的祈祷时间不可用',
      );
    }

    // 请求API并缓存整月数据
    const url = 'example.com';
    final response = await http.post(
      Uri.parse(url),
      headers: {'Content-Type': 'application/json; charset=UTF-8'},
    );

    if (response.statusCode == 200) {
      final responseData = jsonDecode(response.body);
      final monthData = (responseData['prayerTimes'] as List)
          .map((json) => PrayerTimes.fromJson(json))
          .toList();
      
      await box.put(currentMonth, monthData);
      return monthData.firstWhere(
        (pt) => pt.date == currentDate,
        orElse: () => throw '当前日期$currentDate的祈祷时间不可用',
      );
    } else {
      throw '请求失败,状态码:${response.statusCode}';
    }
  }
}

核心逻辑说明

  • 自动刷新:每次请求会对比缓存月份与当前月份,月初时缓存月份为上月,自动触发API请求更新缓存。
  • 缓存有效性:用YYYY-MM作为唯一标识,确保当月内所有请求复用同一缓存。
  • 异常处理:缓存不存在时才请求API,请求失败直接抛出错误(可根据需求调整为返回旧缓存)。

内容的提问来源于stack exchange,提问作者Auvee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:14:54