在这里插入图片描述

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Dio Cache Interceptor是Dio的缓存拦截器库,提供了强大的HTTP缓存功能,支持多种缓存策略和存储方式。通过缓存可以显著提升应用性能,减少网络请求,改善用户体验。

提示:合理使用缓存可以减少网络流量,提高响应速度,支持离线访问。

一、DioCacheInterceptor三方库简介

1.1 什么是Dio Cache Interceptor

Dio Cache Interceptor是Dio的HTTP缓存拦截器,提供灵活的缓存策略。

特性 说明 优势
多种策略 灵活配置 适应场景
多种存储 内存/文件/数据库 可选方案
离线支持 缓存优先 用户体验
性能优化 减少请求 提升速度

官方文档:https://pub.dev/packages/dio_cache_interceptor

GitHub仓库:https://github.com/llfbandit/dio_cache_interceptor

1.2 缓存策略

策略 说明 适用场景
request 仅请求 实时数据
forceCache 强制缓存 静态资源
refresh 刷新缓存 更新数据
refreshForceCache 刷新并缓存 混合场景

1.3 存储方式

存储 说明 特点
MemCacheStore 内存 快速但临时
FileCacheStore 文件 持久化
HiveCacheStore Hive数据库 高性能

二、环境配置

2.1 添加依赖

dependencies:
  dio: ^5.4.0
  dio_cache_interceptor: ^3.5.0
  dio_cache_interceptor_file_store: ^1.2.0

2.2 安装依赖

flutter pub get

2.3 导入包

import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_file_store/dio_cache_interceptor_file_store.dart';

相关资源:

  • Dio Cache Interceptor Package:https://pub.dev/packages/dio_cache_interceptor
  • Dio文档:https://pub.dev/packages/dio
  • HTTP缓存:https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching

三、基础使用

3.1 创建缓存配置

import 'package:path_provider/path_provider.dart';

Future<CacheOptions> createCacheOptions() async {
  final cacheDir = await getTemporaryDirectory();
  
  return CacheOptions(
    store: FileCacheStore(cacheDir.path),
    policy: CachePolicy.request,
    hitCacheOnErrorExcept: [401, 403],
    maxStale: Duration(days: 7),
    priority: CachePriority.normal,
    cipher: null,
    keyBuilder: CacheOptions.defaultCacheKeyBuilder,
    allowPostMethod: false,
  );
}

3.2 添加拦截器

Future<Dio> createDio() async {
  final dio = Dio();
  final cacheOptions = await createCacheOptions();
  
  dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));
  
  return dio;
}

3.3 发送请求

Future<void> fetchData() async {
  final dio = await createDio();
  
  try {
    final response = await dio.get('https://api.example.com/data');
    print('Data: ${response.data}');
    print('From cache: ${response.extra[CacheResponse.key] != null}');
  } catch (e) {
    print('Error: $e');
  }
}

四、缓存策略

4.1 Request策略

// 默认策略:优先网络,失败时使用缓存
final response = await dio.get(
  'https://api.example.com/data',
  options: Options(
    extra: {
      CacheOptions.defaultCacheKeyBuilder: CachePolicy.request,
    },
  ),
);

4.2 ForceCache策略

// 强制使用缓存,不发送网络请求
final response = await dio.get(
  'https://api.example.com/data',
  options: buildCacheOptions(
    Duration(days: 7),
    forceRefresh: false,
  ).toOptions(),
);

4.3 Refresh策略

// 强制刷新,忽略缓存
final response = await dio.get(
  'https://api.example.com/data',
  options: buildCacheOptions(
    Duration(days: 7),
    forceRefresh: true,
  ).toOptions(),
);

4.4 自定义策略

final cacheOptions = CacheOptions(
  store: store,
  policy: CachePolicy.request,
  // 仅在这些错误时使用缓存
  hitCacheOnErrorExcept: [401, 403, 404],
  // 最大过期时间
  maxStale: Duration(days: 7),
  // 优先级
  priority: CachePriority.high,
);

五、存储方式

5.1 内存存储

final cacheOptions = CacheOptions(
  store: MemCacheStore(),
  policy: CachePolicy.request,
  maxStale: Duration(hours: 1),
);

5.2 文件存储

Future<CacheOptions> createFileCacheOptions() async {
  final cacheDir = await getTemporaryDirectory();
  
  return CacheOptions(
    store: FileCacheStore('${cacheDir.path}/dio_cache'),
    policy: CachePolicy.request,
    maxStale: Duration(days: 7),
  );
}

5.3 Hive存储

import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
import 'package:hive/hive.dart';

Future<CacheOptions> createHiveCacheOptions() async {
  final cacheDir = await getApplicationDocumentsDirectory();
  Hive.init(cacheDir.path);
  
  return CacheOptions(
    store: HiveCacheStore(cacheDir.path),
    policy: CachePolicy.request,
    maxStale: Duration(days: 30),
  );
}

六、实战示例

6.1 新闻应用

class NewsService {
  late Dio _dio;
  
  Future<void> initialize() async {
    _dio = Dio();
    
    final cacheDir = await getTemporaryDirectory();
    final cacheOptions = CacheOptions(
      store: FileCacheStore('${cacheDir.path}/news_cache'),
      policy: CachePolicy.request,
      maxStale: Duration(hours: 1),
      hitCacheOnErrorExcept: [401, 403],
    );
    
    _dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));
  }
  
  Future<List<News>> getNews({bool forceRefresh = false}) async {
    try {
      final response = await _dio.get(
        'https://api.example.com/news',
        options: buildCacheOptions(
          Duration(hours: 1),
          forceRefresh: forceRefresh,
        ).toOptions(),
      );
      
      final fromCache = response.extra[CacheResponse.key] != null;
      print('From cache: $fromCache');
      
      return (response.data as List)
          .map((json) => News.fromJson(json))
          .toList();
    } catch (e) {
      print('Error: $e');
      rethrow;
    }
  }
  
  Future<News> getNewsDetail(String id) async {
    final response = await _dio.get(
      'https://api.example.com/news/$id',
      options: buildCacheOptions(
        Duration(days: 1),
      ).toOptions(),
    );
    
    return News.fromJson(response.data);
  }
}

class News {
  final String id;
  final String title;
  final String content;
  
  News({
    required this.id,
    required this.title,
    required this.content,
  });
  
  factory News.fromJson(Map<String, dynamic> json) {
    return News(
      id: json['id'],
      title: json['title'],
      content: json['content'],
    );
  }
}

6.2 图片缓存

class ImageCacheService {
  late Dio _dio;
  
  Future<void> initialize() async {
    _dio = Dio();
    
    final cacheDir = await getTemporaryDirectory();
    final cacheOptions = CacheOptions(
      store: FileCacheStore('${cacheDir.path}/image_cache'),
      policy: CachePolicy.forceCache,
      maxStale: Duration(days: 30),
    );
    
    _dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));
  }
  
  Future<Uint8List> getImage(String url) async {
    final response = await _dio.get<List<int>>(
      url,
      options: Options(
        responseType: ResponseType.bytes,
      ),
    );
    
    return Uint8List.fromList(response.data!);
  }
  
  Future<void> preloadImages(List<String> urls) async {
    await Future.wait(
      urls.map((url) => getImage(url)),
    );
  }
}

6.3 API服务

class ApiService {
  late Dio _dio;
  late CacheStore _cacheStore;
  
  Future<void> initialize() async {
    _dio = Dio(BaseOptions(
      baseUrl: 'https://api.example.com',
      connectTimeout: Duration(seconds: 30),
      receiveTimeout: Duration(seconds: 30),
    ));
    
    final cacheDir = await getTemporaryDirectory();
    _cacheStore = FileCacheStore('${cacheDir.path}/api_cache');
    
    final cacheOptions = CacheOptions(
      store: _cacheStore,
      policy: CachePolicy.request,
      maxStale: Duration(days: 7),
      hitCacheOnErrorExcept: [401, 403],
    );
    
    _dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));
  }
  
  Future<T> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Duration? cacheDuration,
    bool forceRefresh = false,
  }) async {
    final response = await _dio.get(
      path,
      queryParameters: queryParameters,
      options: buildCacheOptions(
        cacheDuration ?? Duration(hours: 1),
        forceRefresh: forceRefresh,
      ).toOptions(),
    );
    
    return response.data as T;
  }
  
  Future<void> clearCache() async {
    await _cacheStore.clean();
  }
  
  Future<void> deleteCache(String key) async {
    await _cacheStore.delete(key);
  }
}

七、高级功能

7.1 自定义Key生成

String customKeyBuilder(RequestOptions request) {
  // 自定义缓存key
  return '${request.method}_${request.uri}_${request.data}';
}

final cacheOptions = CacheOptions(
  store: store,
  keyBuilder: customKeyBuilder,
);

7.2 加密缓存

import 'package:encrypt/encrypt.dart';

final key = Key.fromLength(32);
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));

final cacheOptions = CacheOptions(
  store: store,
  cipher: CacheCipher(
    encrypt: (bytes) => encrypter.encryptBytes(bytes, iv: iv).bytes,
    decrypt: (bytes) => encrypter.decryptBytes(Encrypted(bytes), iv: iv),
  ),
);

7.3 缓存优先级

// 高优先级缓存
final highPriority = CacheOptions(
  store: store,
  priority: CachePriority.high,
  maxStale: Duration(days: 30),
);

// 低优先级缓存
final lowPriority = CacheOptions(
  store: store,
  priority: CachePriority.low,
  maxStale: Duration(hours: 1),
);

7.4 条件缓存

final cacheOptions = CacheOptions(
  store: store,
  policy: CachePolicy.request,
  // 仅缓存GET请求
  allowPostMethod: false,
  // 特定状态码才缓存
  hitCacheOnErrorExcept: [400, 401, 403, 404, 500],
);

八、缓存管理

8.1 清除缓存

class CacheManager {
  final CacheStore store;
  
  CacheManager(this.store);
  
  // 清除所有缓存
  Future<void> clearAll() async {
    await store.clean();
  }
  
  // 清除过期缓存
  Future<void> clearExpired() async {
    await store.clean(staleOnly: true);
  }
  
  // 删除特定缓存
  Future<void> delete(String key) async {
    await store.delete(key);
  }
  
  // 检查缓存是否存在
  Future<bool> exists(String key) async {
    final response = await store.get(key);
    return response != null;
  }
}

8.2 缓存统计

class CacheStats {
  final CacheStore store;
  
  CacheStats(this.store);
  
  Future<Map<String, dynamic>> getStats() async {
    // 实现缓存统计逻辑
    return {
      'totalSize': 0,
      'itemCount': 0,
      'hitRate': 0.0,
    };
  }
}

8.3 缓存预热

class CacheWarmer {
  final Dio dio;
  
  CacheWarmer(this.dio);
  
  Future<void> warmup(List<String> urls) async {
    await Future.wait(
      urls.map((url) => dio.get(url)),
    );
  }
}

九、性能优化

9.1 内存缓存优先

class TieredCacheStore implements CacheStore {
  final MemCacheStore memCache;
  final FileCacheStore fileCache;
  
  TieredCacheStore(this.memCache, this.fileCache);
  
  
  Future<CacheResponse?> get(String key) async {
    // 先查内存
    var response = await memCache.get(key);
    if (response != null) return response;
    
    // 再查文件
    response = await fileCache.get(key);
    if (response != null) {
      // 写入内存缓存
      await memCache.set(response);
    }
    
    return response;
  }
  
  
  Future<void> set(CacheResponse response) async {
    await Future.wait([
      memCache.set(response),
      fileCache.set(response),
    ]);
  }
  
  // 实现其他方法...
}

9.2 压缩缓存

import 'dart:io';

final cacheOptions = CacheOptions(
  store: store,
  // 使用gzip压缩
  cipher: CacheCipher(
    encrypt: (bytes) => gzip.encode(bytes),
    decrypt: (bytes) => gzip.decode(bytes),
  ),
);

9.3 限制缓存大小

class SizeLimitedCacheStore implements CacheStore {
  final CacheStore delegate;
  final int maxSizeBytes;
  int currentSize = 0;
  
  SizeLimitedCacheStore(this.delegate, this.maxSizeBytes);
  
  
  Future<void> set(CacheResponse response) async {
    final size = response.content?.length ?? 0;
    
    if (currentSize + size > maxSizeBytes) {
      await clean(staleOnly: true);
    }
    
    await delegate.set(response);
    currentSize += size;
  }
  
  // 实现其他方法...
}

十、最佳实践

10.1 分类缓存

class CacheConfig {
  static CacheOptions shortTerm() {
    return CacheOptions(
      store: MemCacheStore(),
      maxStale: Duration(minutes: 5),
    );
  }
  
  static Future<CacheOptions> mediumTerm() async {
    final dir = await getTemporaryDirectory();
    return CacheOptions(
      store: FileCacheStore('${dir.path}/medium_cache'),
      maxStale: Duration(hours: 1),
    );
  }
  
  static Future<CacheOptions> longTerm() async {
    final dir = await getApplicationDocumentsDirectory();
    return CacheOptions(
      store: FileCacheStore('${dir.path}/long_cache'),
      maxStale: Duration(days: 30),
    );
  }
}

10.2 错误处理

Future<Response> cachedRequest(String url) async {
  try {
    return await dio.get(url);
  } on DioException catch (e) {
    if (e.type == DioExceptionType.connectionTimeout ||
        e.type == DioExceptionType.receiveTimeout) {
      // 超时时使用缓存
      return await dio.get(
        url,
        options: buildCacheOptions(
          Duration(days: 7),
          forceRefresh: false,
        ).toOptions(),
      );
    }
    rethrow;
  }
}

10.3 缓存策略选择

CachePolicy selectPolicy(String endpoint) {
  if (endpoint.contains('/static/')) {
    return CachePolicy.forceCache;
  } else if (endpoint.contains('/realtime/')) {
    return CachePolicy.refresh;
  } else {
    return CachePolicy.request;
  }
}

十一、常见问题

11.1 缓存未生效

检查:

  • 缓存策略是否正确
  • 存储路径是否有权限
  • 是否添加了拦截器

11.2 缓存占用过大

定期清理:

// 定期清理过期缓存
Timer.periodic(Duration(hours: 24), (_) async {
  await store.clean(staleOnly: true);
});

11.3 离线访问失败

使用forceCache策略:

final response = await dio.get(
  url,
  options: buildCacheOptions(
    Duration(days: 7),
    forceRefresh: false,
  ).toOptions(),
);

十三、性能优化(第二轮)

第二轮缓存优化的核心目标是:更高命中率 + 更低额外开销 + 更可控的缓存体积。缓存不是“开了就快”,需要用数据闭环来治理。

13.1 先建立可观测指标

指标 含义 建议实现
hitRate 命中率 统计fromCache比例
staleRate 过期命中率 统计maxStale相关行为
cacheSize 缓存占用 定期扫描目录/Store统计
p95Latency 95分位耗时 拦截器打点聚合

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

13.2 按接口类型选择策略(减少误用)

接口类型 推荐策略 说明
静态配置/字典 forceCache 高命中、低频更新
列表页 request + maxStale 兼顾实时与体验
实时数据 refresh 强一致优先

十四、内存优化(第二轮)

缓存的内存优化重点是“可控体积”:限制缓存大小、定期清理、避免缓存大对象

14.1 给缓存加上限(按大小/按条数)

class CountLimitedStore implements CacheStore {
  final CacheStore delegate;
  final int maxCount;

  CountLimitedStore(this.delegate, {required this.maxCount});

  
  Future<void> set(CacheResponse response) async {
    // TODO: 真实实现需要维护索引/时间戳
    await delegate.set(response);
  }

  
  Future<CacheResponse?> get(String key) => delegate.get(key);

  
  Future<void> delete(String key) => delegate.delete(key);

  
  Future<void> clean({bool staleOnly = false}) => delegate.clean(staleOnly: staleOnly);
}

14.2 清理策略建议

  • App启动时清理一次过期缓存
  • 每24小时清理过期缓存
  • 当缓存目录超过阈值时触发清理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


十五、问题定位与排障(第二轮)

缓存类问题常见三类:

  • 缓存没命中:key不一致/策略不对/未写入
  • 缓存脏数据:过期策略不合理/forceCache误用
  • 缓存占用过大:未清理/资源类响应缓存过大

15.1 判断响应是否来自缓存

final fromCache = response.extra[CacheResponse.key] != null;
print('fromCache=' + fromCache.toString() + ' url=' + response.requestOptions.uri.toString());

15.2 缓存key排查

  • 打印CacheOptions.defaultCacheKeyBuilder(requestOptions)结果
  • 确保同一接口的query/body不会导致key不断变化

15.3 抓包对齐(缓存命中时是否还发请求)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

十六、总结

本文详细介绍了Dio Cache Interceptor三方库的使用方法,包括缓存策略、存储方式、实战示例等。合理使用缓存可以显著提升应用性能,改善用户体验。

Dio Cache Interceptor的主要优势:

  • 多种缓存策略,灵活配置
  • 多种存储方式,按需选择
  • 支持离线访问
  • 提升应用性能

下一篇文章将继续网络类第二轮主题:把日志、缓存、重试、抓包与服务端对齐流程整合成可落地的“问题定位与性能治理”体系,敬请期待!

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

  • Dio Cache Interceptor官方文档:https://pub.dev/packages/dio_cache_interceptor
  • Dio Cache Interceptor GitHub:https://github.com/llfbandit/dio_cache_interceptor
  • Dio文档:https://pub.dev/packages/dio
  • HTTP缓存:https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching
  • Cache-Control:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
  • File Store:https://pub.dev/packages/dio_cache_interceptor_file_store
  • Hive Store:https://pub.dev/packages/dio_cache_interceptor_hive_store
  • 缓存最佳实践:https://web.dev/http-cache/
  • 离线优先:https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook
  • PWA缓存策略:https://developers.google.com/web/tools/workbox/modules/workbox-strategies
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐