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

如何在离线时队列存储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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:05:15