如何在离线时队列存储API请求,恢复网络后自动执行?(Dio&Retrofit)
实现Dio+Retrofit的离线请求队列方案
完全可以实现,核心思路是通过Dio拦截器捕获请求,结合网络状态判断和本地存储实现请求队列的暂存与自动重发,以下是具体落地步骤:
1. 基础准备
- 引入网络状态监听包(比如
connectivity_plus),实时感知网络切换; - 引入本地存储包(推荐
hive,适合存储复杂对象),持久化离线请求; - 确保Retrofit实例基于自定义配置的Dio对象创建。
2. 定义离线请求实体类
创建可序列化的实体类,保存请求完整信息:
import 'package:dio/dio.dart'; class OfflineRequest { final String method; final String path; final Map<String, dynamic>? headers; final dynamic data; final Map<String, dynamic>? queryParameters; final DateTime timestamp; OfflineRequest({ required this.method, required this.path, this.headers, this.data, this.queryParameters, required this.timestamp, }); // 转成Dio的RequestOptions,方便重发 RequestOptions toRequestOptions() { return RequestOptions( method: method, path: path, headers: headers, data: data, queryParameters: queryParameters, ); } // 序列化与反序列化(适配Hive) Map<String, dynamic> toJson() { return { 'method': method, 'path': path, 'headers': headers, 'data': data, 'queryParameters': queryParameters, 'timestamp': timestamp.toIso8601String(), }; } factory OfflineRequest.fromJson(Map<String, dynamic> json) { return OfflineRequest( method: json['method'], path: json['path'], headers: json['headers'] as Map<String, dynamic>?, data: json['data'], queryParameters: json['queryParameters'] as Map<String, dynamic>?, timestamp: DateTime.parse(json['timestamp']), ); } }
3. 实现Dio离线拦截器
自定义拦截器,处理离线请求暂存与在线请求重发:
import 'package:dio/dio.dart'; import 'package:hive/hive.dart'; class OfflineInterceptor extends Interceptor { final Box<OfflineRequest> offlineRequestBox; OfflineInterceptor(this.offlineRequestBox); @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { // 检查网络状态(替换成你的实际判断逻辑) bool isOffline = await _checkIsOffline(); if (isOffline) { // 保存请求到本地队列 final offlineRequest = OfflineRequest( method: options.method, path: options.path, headers: options.headers, data: options.data, queryParameters: options.queryParameters, timestamp: DateTime.now(), ); await offlineRequestBox.add(offlineRequest); handler.reject(DioError(requestOptions: options, error: "网络离线,请求已加入队列")); } else { // 在线时先重发队列中的请求 await _replayOfflineRequests(); handler.next(options); } } // 按时间顺序重发离线请求 Future<void> _replayOfflineRequests() async { if (offlineRequestBox.isEmpty) return; final requests = offlineRequestBox.values.toList() ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); for (var request in requests) { try { await Dio().fetch(request.toRequestOptions()); // 重发成功,移除队列 await offlineRequestBox.delete(request.key); } catch (e) { // 重发失败,保留请求等待下次重试 break; } } } // 网络状态检查示例(需替换为connectivity_plus的实际逻辑) Future<bool> _checkIsOffline() async { // final result = await Connectivity().checkConnectivity(); // return result == ConnectivityResult.none; return false; } }
4. 配置Dio与Retrofit
将拦截器添加到Dio实例,再用该Dio创建Retrofit:
import 'package:dio/dio.dart'; import 'package:hive/hive.dart'; import 'your_retrofit_api.dart'; void setupDioAndRetrofit() async { // 初始化Hive await Hive.initFlutter(); Hive.registerAdapter(OfflineRequestAdapter()); // 需为OfflineRequest生成Hive适配器 final offlineBox = await Hive.openBox<OfflineRequest>('offline_requests'); // 配置Dio final dio = Dio(BaseOptions(baseUrl: "your_base_url")); dio.interceptors.add(OfflineInterceptor(offlineBox)); // 创建Retrofit实例 final api = RestClient(dio); }
5. 监听网络状态切换
App启动时监听网络,恢复连接时主动触发队列重发:
import 'package:connectivity_plus/connectivity_plus.dart'; void setupNetworkListener(Box<OfflineRequest> offlineBox) { Connectivity().onConnectivityChanged.listen((result) { if (result != ConnectivityResult.none) { // 网络恢复,触发重发 OfflineInterceptor(offlineBox)._replayOfflineRequests(); } }); }
注意事项
- 请求去重:添加队列前可检查是否已有相同参数的请求,避免重复存储;
- 重试机制:重发失败可设置重试次数,或保留请求等待下次网络恢复;
- 数据序列化:确保请求体(data)为可序列化类型(如Map、String),避免存储失败;
- 权限配置:确保App拥有网络访问和本地存储权限。
内容的提问来源于stack exchange,提问作者Vikas S
相关产品推荐
相关产品推荐

