Flutter网络请求进阶:Mock模拟、联调优化与性能监控实战

在企业级Flutter应用的开发与测试流程中,“接口未就绪时的Mock数据模拟”“多端联调的效率优化”“网络请求性能的精准监控”是提升开发效率、保障应用性能的关键环节。传统开发模式中,前端依赖后端接口就绪才能推进开发,联调过程中问题定位繁琐,网络性能瓶颈难以察觉。本文聚焦这三大核心痛点,提供“本地Mock+远程Mock”双模式模拟、联调调试工具集成、全链路性能指标采集与分析的实战方案,覆盖从开发、联调到上线前性能校验的全流程需求。

一、核心设计思路:提效与质控并行

本次方案遵循三大核心设计原则,兼顾开发效率与质量控制:

  • Mock分层与灵活切换原则:支持“本地Mock(开发离线可用)”与“远程Mock(多端共享)”,可按接口、按环境动态切换Mock/真实接口,不侵入业务代码,保障开发与联调的顺畅衔接。

  • 联调高效定位原则:集成请求日志可视化、接口数据抓包、异常快速定位功能,支持一键复制请求信息、模拟异常场景,降低多端联调的沟通与定位成本。

  • 性能全链路监控原则:采集网络请求全生命周期的关键性能指标(发起耗时、DNS解析耗时、响应耗时、数据处理耗时等),结合环境、网络类型维度分析,精准定位性能瓶颈。

二、Mock数据模拟:本地+远程双模式实现

Mock数据模拟的核心是“不依赖真实接口推进开发”,本节实现支持本地静态Mock、本地动态逻辑Mock与远程Mock服务对接的双模式方案,支持灵活切换与精准匹配。

1. 基础准备:Mock配置与数据模型

定义Mock策略枚举、Mock配置模型,明确各Mock模式的适用场景,为后续Mock逻辑提供基础。


// Mock策略枚举
enum MockStrategy {
  disable, // 禁用Mock(使用真实接口)
  localStatic, // 本地静态Mock(返回固定JSON数据)
  localDynamic, // 本地动态Mock(根据请求参数生成动态数据)
  remote, // 远程Mock(对接Mock服务,如Mock.js、RAP2)
}

// Mock响应数据模型
class MockResponse {
  final int statusCode; // 响应状态码
  final Map<String, dynamic> data; // 响应数据
  final Map<String, String> headers; // 响应头
  final Duration delay; // 模拟网络延迟

  MockResponse({
    this.statusCode = 200,
    required this.data,
    this.headers = const {},
    this.delay = const Duration(milliseconds: 300),
  });
}

// 单个接口的Mock配置
class ApiMockConfig {
  final MockStrategy strategy;
  // 本地静态Mock数据(仅localStatic策略有效)
  final MockResponse? staticResponse;
  // 本地动态Mock生成器(仅localDynamic策略有效)
  final MockResponse Function(RequestOptions options)? dynamicResponseGenerator;
  // 远程Mock服务URL(仅remote策略有效,默认使用全局Mock服务URL)
  final String? remoteMockUrl;

  ApiMockConfig({
    this.strategy = MockStrategy.disable,
    this.staticResponse,
    this.dynamicResponseGenerator,
    this.remoteMockUrl,
  });
}

2. 实现本地Mock工具类:静态+动态支持

封装本地Mock工具类,负责加载本地静态Mock数据、执行动态Mock逻辑,支持按接口路径精准匹配Mock配置。


import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/services.dart';
import 'mock_model.dart';

// 本地Mock工具类
class LocalMockUtil {
  // 存储接口Mock配置(key:接口路径+方法,value:Mock配置)
  final Map<String, ApiMockConfig> _apiMockConfigs = {};
  // 本地静态Mock数据缓存(key:文件路径,value:解析后的JSON)
  final Map<String, Map<String, dynamic>> _staticMockCache = {};

  // 注册单个接口的Mock配置
  void registerApiMockConfig({
    required String path,
    required String method,
    required ApiMockConfig config,
  }) {
    final key = '$method:$path';
    _apiMockConfigs[key] = config;
  }

  // 批量注册接口Mock配置
  void registerBatchApiMockConfigs(Map<String, ApiMockConfig> configs) {
    _apiMockConfigs.addAll(configs);
  }

  // 获取接口的Mock配置
  ApiMockConfig? getApiMockConfig(RequestOptions options) {
    final key = '${options.method}:${options.path}';
    return _apiMockConfigs[key];
  }

  // 加载本地静态Mock文件(assets/mock目录下的JSON文件)
  Future<Map<String, dynamic>> loadStaticMockFile(String filePath) async {
    if (_staticMockCache.containsKey(filePath)) {
      return _staticMockCache[filePath]!;
    }
    final jsonString = await rootBundle.loadString('assets/mock/$filePath');
    final jsonData = json.decode(jsonString) as Map<String, dynamic>;
    _staticMockCache[filePath] = jsonData;
    return jsonData;
  }

  // 生成本地Mock响应
  Future<MockResponse> generateLocalMockResponse(RequestOptions options) async {
    final config = getApiMockConfig(options);
    if (config == null) {
      throw Exception('未找到接口${options.path}的Mock配置');
    }

    switch (config.strategy) {
      case MockStrategy.localStatic:
        final staticResponse = config.staticResponse;
        if (staticResponse == null) {
          throw Exception('本地静态Mock配置缺失响应数据');
        }
        // 模拟网络延迟
        await Future.delayed(staticResponse.delay);
        return staticResponse;
      case MockStrategy.localDynamic:
        final generator = config.dynamicResponseGenerator;
        if (generator == null) {
          throw Exception('本地动态Mock配置缺失生成器');
        }
        final dynamicResponse = generator(options);
        // 模拟网络延迟
        await Future.delayed(dynamicResponse.delay);
        return dynamicResponse;
      default:
        throw Exception('不支持的本地Mock策略:${config.strategy.name}');
    }
  }
}

3. 实现Mock拦截器:请求自动Mock与切换

通过Dio拦截器整合本地Mock工具与远程Mock服务,实现请求的自动Mock匹配与响应生成,支持全局Mock配置与单个请求Mock配置的灵活叠加。


import 'dart:convert';
import 'package:dio/dio.dart';
import 'mock_model.dart';
import 'local_mock_util.dart';

// 全局Mock配置
class GlobalMockConfig {
  final MockStrategy defaultStrategy; // 全局默认Mock策略
  final String? remoteMockBaseUrl; // 远程Mock服务基础URL
  final bool enableMock; // 是否全局启用Mock

  GlobalMockConfig({
    this.defaultStrategy = MockStrategy.disable,
    this.remoteMockBaseUrl,
    this.enableMock = true,
  });
}

// Mock拦截器
class MockInterceptor extends Interceptor {
  final GlobalMockConfig _globalConfig;
  final LocalMockUtil _localMockUtil = LocalMockUtil();
  late Dio _mockDio; // 用于请求远程Mock服务

  MockInterceptor({
    required GlobalMockConfig globalConfig,
  }) : _globalConfig = globalConfig {
    _mockDio = Dio();
    if (globalConfig.remoteMockBaseUrl != null) {
      _mockDio.options.baseUrl = globalConfig.remoteMockBaseUrl!;
    }
  }

  @override
  Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    // 1. 全局禁用Mock,直接放行
    if (!_globalConfig.enableMock) {
      handler.next(options);
      return;
    }

    // 2. 获取当前请求的Mock配置(优先使用请求自带的配置,无则使用全局默认)
    final requestMockConfig = options.extra['mockConfig'] as ApiMockConfig? ??
        ApiMockConfig(strategy: _globalConfig.defaultStrategy);

    // 3. 禁用Mock,直接放行
    if (requestMockConfig.strategy == MockStrategy.disable) {
      handler.next(options);
      return;
    }

    try {
      // 4. 处理不同Mock策略
      MockResponse mockResponse;
      switch (requestMockConfig.strategy) {
        case MockStrategy.localStatic:
        case MockStrategy.localDynamic:
          // 本地Mock
          mockResponse = await _localMockUtil.generateLocalMockResponse(options);
          break;
        case MockStrategy.remote:
          // 远程Mock:转发请求到远程Mock服务
          final remoteUrl = requestMockConfig.remoteMockUrl ??
              '${_globalConfig.remoteMockBaseUrl}${options.path}';
          final mockResponseData = await _mockDio.request(
            remoteUrl,
            method: options.method,
            queryParameters: options.queryParameters,
            data: options.data,
            options: Options(headers: options.headers),
          );
          mockResponse = MockResponse(
            statusCode: mockResponseData.statusCode ?? 200,
            data: mockResponseData.data as Map<String, dynamic>,
            headers: mockResponseData.headers.map.map((key, value) => MapEntry(key, value.join(','))),
            delay: const Duration(milliseconds: 300),
          );
          break;
        default:
          throw Exception('不支持的Mock策略:${requestMockConfig.strategy.name}');
      }

      // 5. 构造Mock响应,直接返回
      handler.resolve(
        Response(
          requestOptions: options,
          statusCode: mockResponse.statusCode,
          data: mockResponse.data,
          headers: Headers.fromMap(mockResponse.headers),
        ),
      );
    } catch (e) {
      // Mock失败,放行到真实接口
      print('Mock失败:$e,将请求真实接口');
      handler.next(options);
    }
  }

  // 注册本地Mock配置(对外提供注册入口)
  void registerApiMockConfig({
    required String path,
    required String method,
    required ApiMockConfig config,
  }) {
    _localMockUtil.registerApiMockConfig(path: path, method: method, config: config);
  }

  // 批量注册本地Mock配置
  void registerBatchApiMockConfigs(Map<String, ApiMockConfig> configs) {
    _localMockUtil.registerBatchApiMockConfigs(configs);
  }
}

三、联调优化:调试工具集成与问题定位

联调阶段的核心需求是“快速定位问题”,本节实现请求日志可视化、接口数据抓包、异常模拟等调试功能,集成第三方调试工具,提升联调效率。

1. 实现调试日志拦截器:请求信息全记录

封装调试日志拦截器,记录请求全链路的详细信息(请求参数、响应数据、耗时、网络状态等),支持日志过滤、一键复制,便于问题排查。


import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';

// 调试日志级别
enum DebugLogLevel {
  verbose, // 详细(记录所有信息)
  info, // 信息(记录请求/响应基本信息)
  error, // 错误(仅记录异常信息)
}

// 调试日志模型
class DebugLogModel {
  final String id; // 日志唯一ID
  final String time; // 记录时间
  final String method; // 请求方法
  final String path; // 接口路径
  final String url; // 完整URL
  final Map<String, dynamic>? requestParams; // 请求参数
  final Map<String, dynamic>? requestData; // 请求体
  final Map<String, String>? requestHeaders; // 请求头
  final int? statusCode; // 响应状态码
  final Map<String, dynamic>? responseData; // 响应数据
  final Map<String, String>? responseHeaders; // 响应头
  final String? errorMessage; // 错误信息
  final Duration? duration; // 请求耗时
  final String? networkType; // 网络类型(Wi-Fi/mobile/none)

  DebugLogModel({
    required this.id,
    required this.time,
    required this.method,
    required this.path,
    required this.url,
    this.requestParams,
    this.requestData,
    this.requestHeaders,
    this.statusCode,
    this.responseData,
    this.responseHeaders,
    this.errorMessage,
    this.duration,
    this.networkType,
  });

  // 转换为JSON字符串(便于复制)
  String toJsonString() {
    return json.encode({
      'id': id,
      'time': time,
      'method': method,
      'path': path,
      'url': url,
      'requestParams': requestParams,
      'requestData': requestData,
      'requestHeaders': requestHeaders,
      'statusCode': statusCode,
      'responseData': responseData,
      'responseHeaders': responseHeaders,
      'errorMessage': errorMessage,
      'duration': duration?.inMilliseconds,
      'networkType': networkType,
    }, indent: 2);
  }
}

// 调试日志拦截器
class DebugLogInterceptor extends Interceptor {
  final DebugLogLevel logLevel;
  final bool enableLog;
  final bool enableVisualization; // 是否启用日志可视化(如弹窗展示)
  final void Function(DebugLogModel log)? onLogGenerated; // 日志生成回调(用于可视化)

  // 存储请求开始时间(用于计算耗时)
  final Map<String, DateTime> _requestStartTime = {};

  DebugLogInterceptor({
    this.logLevel = DebugLogLevel.verbose,
    this.enableLog = true,
    this.enableVisualization = false,
    this.onLogGenerated,
  });

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (!enableLog) {
      handler.next(options);
      return;
    }

    // 记录请求开始时间
    final requestId = _generateRequestId();
    _requestStartTime[requestId] = DateTime.now();

    // 生成请求日志(根据级别过滤)
    if (logLevel == DebugLogLevel.verbose || logLevel == DebugLogLevel.info) {
      final log = DebugLogModel(
        id: requestId,
        time: DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(DateTime.now()),
        method: options.method,
        path: options.path,
        url: options.uri.toString(),
        requestParams: options.queryParameters,
        requestData: options.data is Map ? options.data : json.decode(options.data.toString()),
        requestHeaders: options.headers.map.map((key, value) => MapEntry(key, value.toString())),
        networkType: options.extra['networkType'] as String?,
      );

      _printLog(log);
      if (enableVisualization && onLogGenerated != null) {
        onLogGenerated!(log);
      }
    }

    handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    if (!enableLog) {
      handler.next(response);
      return;
    }

    // 计算请求耗时
    final requestId = _generateRequestIdFromOptions(response.requestOptions);
    final startTime = _requestStartTime[requestId];
    final duration = startTime != null ? DateTime.now().difference(startTime) : null;
    _requestStartTime.remove(requestId);

    // 生成响应日志(根据级别过滤)
    if (logLevel == DebugLogLevel.verbose || logLevel == DebugLogLevel.info) {
      final log = DebugLogModel(
        id: requestId,
        time: DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(DateTime.now()),
        method: response.requestOptions.method,
        path: response.requestOptions.path,
        url: response.requestOptions.uri.toString(),
        requestParams: response.requestOptions.queryParameters,
        requestData: response.requestOptions.data is Map
            ? response.requestOptions.data
            : json.decode(response.requestOptions.data.toString()),
        requestHeaders: response.requestOptions.headers.map.map((key, value) => MapEntry(key, value.toString())),
        statusCode: response.statusCode,
        responseData: response.data,
        responseHeaders: response.headers.map.map((key, value) => MapEntry(key, value.join(','))),
        duration: duration,
        networkType: response.requestOptions.extra['networkType'] as String?,
      );

      _printLog(log);
      if (enableVisualization && onLogGenerated != null) {
        onLogGenerated!(log);
      }
    }

    handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    if (!enableLog) {
      handler.next(err);
      return;
    }

    // 计算请求耗时
    final requestId = _generateRequestIdFromOptions(err.requestOptions);
    final startTime = _requestStartTime[requestId];
    final duration = startTime != null ? DateTime.now().difference(startTime) : null;
    _requestStartTime.remove(requestId);

    // 生成错误日志(所有级别都记录)
    final log = DebugLogModel(
      id: requestId,
      time: DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(DateTime.now()),
      method: err.requestOptions.method,
      path: err.requestOptions.path,
      url: err.requestOptions.uri.toString(),
      requestParams: err.requestOptions.queryParameters,
      requestData: err.requestOptions.data is Map
          ? err.requestOptions.data
          : json.decode(err.requestOptions.data.toString()),
      requestHeaders: err.requestOptions.headers.map.map((key, value) => MapEntry(key, value.toString())),
      statusCode: err.response?.statusCode,
      responseData: err.response?.data,
      errorMessage: err.toString(),
      duration: duration,
      networkType: err.requestOptions.extra['networkType'] as String?,
    );

    _printLog(log);
    if (enableVisualization && onLogGenerated != null) {
      onLogGenerated!(log);
    }

    handler.next(err);
  }

  // 生成请求ID(基于时间+随机数)
  String _generateRequestId() {
    final time = DateTime.now().millisecondsSinceEpoch.toString();
    final random = DateTime.now().microsecond.toString().padLeft(6, '0');
    return '$time$random';
  }

  // 从请求选项生成请求ID(用于匹配请求与响应)
  String _generateRequestIdFromOptions(RequestOptions options) {
    return _generateRequestId(); // 实际可优化为基于请求参数生成固定ID,此处简化
  }

  // 打印日志(区分环境:调试环境打印,生产环境禁用)
  void _printLog(DebugLogModel log) {
    if (kDebugMode) {
      print('\n===== 网络调试日志 [${log.id}] =====');
      print('时间:${log.time}');
      print('请求:${log.method} ${log.path}');
      print('URL:${log.url}');
      if (log.requestParams != null && logLevel == DebugLogLevel.verbose) {
        print('请求参数:${json.encode(log.requestParams, indent: 2)}');
      }
      if (log.requestData != null && logLevel == DebugLogLevel.verbose) {
        print('请求体:${json.encode(log.requestData, indent: 2)}');
      }
      if (log.statusCode != null) {
        print('状态码:${log.statusCode}');
      }
      if (log.responseData != null && logLevel == DebugLogLevel.verbose) {
        print('响应数据:${json.encode(log.responseData, indent: 2)}');
      }
      if (log.errorMessage != null) {
        print('错误信息:${log.errorMessage}');
      }
      if (log.duration != null) {
        print('耗时:${log.duration?.inMilliseconds}ms');
      }
      print('====================================\n');
    }
  }
}

2. 集成第三方调试工具:增强联调能力

集成dio_logflutter_debug_panel等第三方调试工具,实现请求日志的可视化展示、接口数据的快速复制、Mock数据的动态切换,进一步提升联调效率。


import 'package:dio/dio.dart';
import 'package:dio_log/dio_log.dart';
import 'debug_log_interceptor.dart';

class DebugToolIntegration {
  // 为Dio实例集成调试工具
  static void integrate(Dio dio) {
    // 1. 集成dio_log(日志可视化面板)
    dio.interceptors.add(DioLogInterceptor(
      requestShowLoading: false, // 不显示请求加载中弹窗
      responseShowLoading: false, // 不显示响应加载中弹窗
      maxWidth: 800, // 日志面板最大宽度
      showResponseHeader: true, // 显示响应头
      showRequestHeader: true, // 显示请求头
      showRequestBody: true, // 显示请求体
      showResponseBody: true, // 显示响应体
    ));

    // 2. 集成自定义调试日志拦截器(补充自定义日志逻辑)
    dio.interceptors.add(DebugLogInterceptor(
      logLevel: DebugLogLevel.verbose,
      enableLog: true,
      enableVisualization: true,
      onLogGenerated: (log) {
        // 可在此处将日志发送到自定义的调试面板
        print('自定义日志回调:${log.toJsonString()}');
      },
    ));

    // 3. 集成网络状态传递(为日志添加网络类型信息)
    dio.interceptors.add(
      Interceptor() {
        @override
        void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
          // 从NetworkMonitor获取当前网络类型(复用之前的NetworkMonitor)
          final networkType = NetworkMonitor().currentState.type.name;
          options.extra['networkType'] = networkType;
          handler.next(options);
        }
      },
    );
  }
}

四、性能监控:全链路指标采集与分析

网络性能是影响应用体验的关键因素,本节实现网络请求全生命周期的性能指标采集,包括DNS解析耗时、TCP连接耗时、请求发送耗时、响应接收耗时等,支持本地分析与远程上报。

1. 基础准备:性能指标模型

定义网络性能指标模型,明确需要采集的关键指标,为后续采集与分析提供基础。


// 网络性能指标模型
class NetworkPerformanceMetric {
  final String requestId; // 请求唯一ID
  final String path; // 接口路径
  final String method; // 请求方法
  final String networkType; // 网络类型(Wi-Fi/mobile/none)
  final DateTime requestStartTime; // 请求开始时间
  final DateTime? dnsResolveEndTime; // DNS解析结束时间
  final DateTime? tcpConnectEndTime; // TCP连接结束时间
  final DateTime? requestSendEndTime; // 请求发送结束时间
  final DateTime? responseStart_time; // 响应开始接收时间
  final DateTime responseEndTime; // 响应接收结束时间

  // 计算各阶段耗时(毫秒)
  int? get dnsResolveDuration => dnsResolveEndTime != null
      ? dnsResolveEndTime!.difference(requestStartTime).inMilliseconds
      : null;

  int? get tcpConnectDuration => dnsResolveEndTime != null && tcpConnectEndTime != null
      ? tcpConnectEndTime!.difference(dnsResolveEndTime!).inMilliseconds
      : null;

  int? get requestSendDuration => tcpConnectEndTime != null && requestSendEndTime != null
      ? requestSendEndTime!.difference(tcpConnectEndTime!).inMilliseconds
      : null;

  int? get responseReceiveDuration => requestSendEndTime != null && responseStart_time != null
      ? responseStart_time!.difference(requestSendEndTime!).inMilliseconds
      : null;

  int get totalDuration => responseEndTime.difference(requestStartTime).inMilliseconds;

  NetworkPerformanceMetric({
    required this.requestId,
    required this.path,
    required this.method,
    required this.networkType,
    required this.requestStartTime,
    this.dnsResolveEndTime,
    this.tcpConnectEndTime,
    this.requestSendEndTime,
    this.responseStart_time,
    required this.responseEndTime,
  });

  // 转换为JSON(用于上报)
  Map<String, dynamic> toJson() {
    return {
      'requestId': requestId,
      'path': path,
      'method': method,
      'networkType': networkType,
      'requestStartTime': requestStartTime.millisecondsSinceEpoch,
      'dnsResolveEndTime': dnsResolveEndTime?.millisecondsSinceEpoch,
      'tcpConnectEndTime': tcpConnectEndTime?.millisecondsSinceEpoch,
      'requestSendEndTime': requestSendEndTime?.millisecondsSinceEpoch,
      'responseStartTime': responseStart_time?.millisecondsSinceEpoch,
      'responseEndTime': responseEndTime.millisecondsSinceEpoch,
      'dnsResolveDuration': dnsResolveDuration,
      'tcpConnectDuration': tcpConnectDuration,
      'requestSendDuration': requestSendDuration,
      'responseReceiveDuration': responseReceiveDuration,
      'totalDuration': totalDuration,
    };
  }
}

2. 实现性能监控拦截器:指标采集

通过Dio拦截器结合自定义的HTTP客户端适配器,采集网络请求各阶段的时间点,计算关键性能指标。


import 'dart:async';
import 'package:dio/dio.dart';
import 'package:http_client_hoc081098/http_client_hoc081098.dart';
import 'network_performance_model.dart';
import 'network_monitor.dart';

// 性能监控拦截器
class PerformanceMonitorInterceptor extends Interceptor {
  final StreamController<NetworkPerformanceMetric> _metricController =
      StreamController<NetworkPerformanceMetric>.broadcast();
  final Map<String, DateTime> _requestStartTimes = {};
  final Map<String, Map<String, DateTime>> _stageTimes = {}; // 存储各阶段时间点

  // 性能指标流(外部可监听)
  Stream<NetworkPerformanceMetric> get metricStream => _metricController.stream;

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    final requestId = _generateRequestId();
    final startTime = DateTime.now();
    _requestStartTimes[requestId] = startTime;
    _stageTimes[requestId] = {'requestStart': startTime};
    options.extra['requestId'] = requestId;
    handler.next(options);
  }

  // 自定义HTTP客户端适配器,用于采集各阶段时间点
  HttpClientAdapter createPerformanceAdapter(HttpClientAdapter adapter) {
    return _PerformanceHttpClientAdapter(
      adapter: adapter,
      onStageComplete: (requestId, stage, time) {
        if (_stageTimes.containsKey(requestId)) {
          _stageTimes[requestId]![stage] = time;
        }
      },
    );
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    final requestId = response.requestOptions.extra['requestId'] as String?;
    if (requestId == null || !_requestStartTimes.containsKey(requestId)) {
      handler.next(response);
      return;
    }

    // 记录响应结束时间
    final responseEndTime = DateTime.now();
    _stageTimes[requestId]!['responseEnd'] = responseEndTime;

    // 构建性能指标
    final stageTimes = _stageTimes[requestId]!;
    final networkType = NetworkMonitor().currentState.type.name;
    final metric = NetworkPerformanceMetric(
      requestId: requestId,
      path: response.requestOptions.path,
      method: response.requestOptions.method,
      networkType: networkType,
      requestStartTime: stageTimes['requestStart']!,
      dnsResolveEndTime: stageTimes['dnsResolveEnd'],
      tcpConnectEndTime: stageTimes['tcpConnectEnd'],
      requestSendEndTime: stageTimes['requestSendEnd'],
      responseStart_time: stageTimes['responseStart'],
      responseEndTime: responseEndTime,
    );

    // 发送指标到流(供外部监听)
    _metricController.add(metric);

    // 上报性能指标(如上传到服务端)
    _reportMetric(metric);

    // 清理缓存
    _requestStartTimes.remove(requestId);
    _stageTimes.remove(requestId);

    handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    // 异常场景也记录性能指标(仅记录到异常发生时的阶段)
    final requestId = err.requestOptions.extra['requestId'] as String?;
    if (requestId != null && _requestStartTimes.containsKey(requestId)) {
      final responseEndTime = DateTime.now();
      final stageTimes = _stageTimes[requestId]!;
      final networkType = NetworkMonitor().currentState.type.name;
      final metric = NetworkPerformanceMetric(
        requestId: requestId,
        path: err.requestOptions.path,
        method: err.requestOptions.method,
        networkType: networkType,
        requestStartTime: stageTimes['requestStart']!,
        dnsResolveEndTime: stageTimes['dnsResolveEnd'],
        tcpConnectEndTime: stageTimes['tcpConnectEnd'],
        requestSendEndTime: stageTimes['requestSendEnd'],
        responseStart_time: stageTimes['responseStart'],
        responseEndTime: responseEndTime,
      );
      _metricController.add(metric);
      _reportMetric(metric);
      _requestStartTimes.remove(requestId);
      _stageTimes.remove(requestId);
    }

    handler.next(err);
  }

  // 生成请求ID
  String _generateRequestId() {
    final time = DateTime.now().millisecondsSinceEpoch.toString();
    final random = DateTime.now().microsecond.toString().padLeft(6, '0');
    return '$time$random';
  }

  // 上报性能指标
  Future<void> _reportMetric(NetworkPerformanceMetric metric) async {
    try {
      // 实际项目中,上报到服务端的性能监控接口
      final dio = Dio();
      await dio.post(
        'https://monitor.your-domain.com/network/performance',
        data: metric.toJson(),
        options: Options(
          sendTimeout: const Duration(milliseconds: 5000),
        ),
      );
    } catch (e) {
      print('性能指标上报失败:$e');
    }
  }

  // 释放资源
  void dispose() {
    _metricController.close();
  }
}

// 自定义HTTP客户端适配器,采集各阶段时间点
class _PerformanceHttpClientAdapter extends HttpClientAdapter {
  final HttpClientAdapter adapter;
  final void Function(String requestId, String stage, DateTime time) onStageComplete;

  _PerformanceHttpClientAdapter({
    required this.adapter,
    required this.onStageComplete,
  });

  @override
  Future<ResponseBody> fetch(RequestOptions options, Stream<List<int>> requestStream, Future<void> cancelFuture) async {
    final requestId = options.extra['requestId'] as String? ?? '';

    // 1. DNS解析结束(简化:实际需Hook DNS解析过程,此处用连接开始前的时间模拟)
    onStageComplete(requestId, 'dnsResolveEnd', DateTime.now());

    // 2. TCP连接结束(简化:实际需Hook TCP连接过程)
    onStageComplete(requestId, 'tcpConnectEnd', DateTime.now());

    // 3. 请求发送结束
    final requestSendEnd = DateTime.now();
    onStageComplete(requestId, 'requestSendEnd', requestSendEnd);

    // 4. 响应开始接收
    final responseStart = DateTime.now();
    onStageComplete(requestId, 'responseStart', responseStart);

    // 执行原始请求
    return adapter.fetch(options, requestStream, cancelFuture);
  }

  @override
  void close({bool force = false}) {
    adapter.close(force: force);
  }
}

3. 整合所有组件:构建开发-联调-性能监控体系

将Mock拦截器、调试日志拦截器、性能监控拦截器整合到NetworkUtil中,形成覆盖开发、联调、性能监控全流程的网络体系。


import 'package:dio/dio.dart';
import 'mock_interceptor.dart';
import 'debug_log_interceptor.dart';
import 'performance_monitor_interceptor.dart';
import 'adaptive_network_util.dart'; // 复用之前的自适应网络工具
import 'mock_model.dart';

class DevDebugNetworkUtil {
  static final DevDebugNetworkUtil _instance = DevDebugNetworkUtil._internal();
  factory DevDebugNetworkUtil() => _instance;
  late AdaptiveNetworkUtil _adaptiveNetworkUtil;
  late MockInterceptor _mockInterceptor;
  late PerformanceMonitorInterceptor _performanceInterceptor;

  DevDebugNetworkUtil._internal() {
    _adaptiveNetworkUtil = AdaptiveNetworkUtil();
    _initDevDebugComponents();
  }

  // 初始化开发调试相关组件
  void _initDevDebugComponents() {
    final dio = _adaptiveNetworkUtil._dio; // 假设AdaptiveNetworkUtil暴露dio实例,实际项目中可通过方法获取

    // 1. 初始化Mock拦截器
    _mockInterceptor = MockInterceptor(
      globalConfig: GlobalMockConfig(
        defaultStrategy: MockStrategy.disable,
        remoteMockBaseUrl: 'https://mock.your-domain.com',
        enableMock: true,
      ),
    );
    dio.interceptors.add(_mockInterceptor);

    // 2. 注册本地Mock配置(示例:首页列表接口)
    _mockInterceptor.registerApiMockConfig(
      path: '/api/home/list',
      method: 'GET',
      config: ApiMockConfig(
        strategy: MockStrategy.localStatic,
        staticResponse: MockResponse(
          data: {
            'code': 200,
            'message': 'success',
            'data': [
              {'id': 1, 'title': 'Mock标题1', 'content': 'Mock内容1'},
              {'id': 2, 'title': 'Mock标题2', 'content': 'Mock内容2'},
            ],
          },
          delay: const Duration(milliseconds: 500),
        ),
      ),
    );

    // 3. 集成调试工具
    DebugToolIntegration.integrate(dio);

    // 4. 初始化性能监控拦截器
    _performanceInterceptor = PerformanceMonitorInterceptor();
    dio.interceptors.add(_performanceInterceptor);

    // 5. 替换Dio的HTTP适配器为性能监控适配器
    dio.httpClientAdapter = _performanceInterceptor.createPerformanceAdapter(dio.httpClientAdapter);

    // 6. 监听性能指标(本地分析)
    _performanceInterceptor.metricStream.listen((metric) {
      print('\n===== 网络性能指标 =====');
      print('接口:${metric.method} ${metric.path}');
      print('网络类型:${metric.networkType}');
      print('总耗时:${metric.totalDuration}ms');
      print('DNS解析耗时:${metric.dnsResolveDuration}ms');
      print('TCP连接耗时:${metric.tcpConnectDuration}ms');
      print('请求发送耗时:${metric.requestSendDuration}ms');
      print('响应接收耗时:${metric.responseReceiveDuration}ms');
      print('======================\n');

      // 性能告警(如总耗时超过3秒)
      if (metric.totalDuration > 3000) {
        print('⚠️  性能告警:${metric.path} 接口耗时过长(${metric.totalDuration}ms)');
      }
    });
  }

  // 对外提供请求方法(复用AdaptiveNetworkUtil的请求方法,增加Mock配置参数)
  Future<T?> request<T>(
    String path, {
    required String method,
    Map<String, dynamic>? queryParams,
    dynamic data,
    Options? options,
    ApiMockConfig? mockConfig,
    // 其他配置参数...
  }) async {
    final extra = <String, dynamic>{};
    if (mockConfig != null) {
      extra['mockConfig'] = mockConfig;
    }
    // 其他配置参数处理...

    final requestOptions = Options(
      method: method,
      ...options,
      extra: {
        ...options?.extra ?? {},
        ...extra,
      },
    );

    return _adaptiveNetworkUtil.request<T>(
      path,
      method: method,
      queryParams: queryParams,
      data: data,
      options: requestOptions,
      // 其他配置参数传递...
    );
  }

  // 快速切换全局Mock策略
  void switchGlobalMockStrategy(MockStrategy strategy) {
    _mockInterceptor._globalConfig = _mockInterceptor._globalConfig.copyWith(
      defaultStrategy: strategy,
    );
  }

  // 释放资源
  void dispose() {
    _performanceInterceptor.dispose();
  }
}

final devDebugNetUtil = DevDebugNetworkUtil();

五、实战场景:开发-联调-性能优化流程

1. 场景1:接口未就绪时的本地Mock开发


// 开发阶段:使用本地Mock数据推进开发
Future<void> developWithLocalMock() async {
  try {
    // 调用接口时,指定本地动态Mock策略
    final data = await devDebugNetUtil.request<Map<String, dynamic>>(
      '/api/user/profile',
      method: 'GET',
      mockConfig: ApiMockConfig(
        strategy: MockStrategy.localDynamic,
        dynamicResponseGenerator: (options) {
          // 根据请求参数生成动态数据(如根据userId返回不同用户信息)
          final userId = options.queryParameters['userId'] as String? ?? '1001';
          return MockResponse(
            data: {
              'code': 200,
              'message': 'success',
              'data': {
                'userId': userId,
                'name': 'Mock用户_$userId',
                'avatar': 'https://mock-avatar.com/$userId.png',
                'age': 25 + int.parse(userId.substring(userId.length - 2)),
              },
            },
          );
        },
      ),
    );
    // 基于Mock数据开发UI
    print('Mock用户信息:$data');
  } catch (e) {
    print('开发阶段请求失败:$e');
  }
}

2. 场景2:多端联调时的问题定位


// 联调阶段:开启调试日志,快速定位问题
Future<void> debugInJointDebug() async {
  try {
    final data = await devDebugNetUtil.request<Map<String, dynamic>>(
      '/api/order/submit',
      method: 'POST',
      data: {
        'orderId': 'ORDER123456',
        'amount': 99.9,
      },
      // 联调时禁用Mock,使用真实接口
      mockConfig: ApiMockConfig(strategy: MockStrategy.disable),
    );
    print('下单成功:$data');
  } catch (e) {
    // 查看调试日志中的请求参数、响应数据、错误信息
    print('下单失败,查看调试日志定位问题:$e');
    // 一键复制日志到剪贴板,发送给后端排查
  }
}

3. 场景3:上线前的性能优化


// 性能优化阶段:分析性能指标,定位瓶颈
void optimizePerformance() {
  // 监听性能指标,分析耗时较长的接口
  devDebugNetUtil._performanceInterceptor.metricStream.listen((metric) {
    // 筛选耗时超过2秒的接口
    if (metric.totalDuration > 2000) {
      print('性能优化目标:${metric.method} ${metric.path}(耗时:${metric.totalDuration}ms)');
      // 分析各阶段耗时,定位瓶颈
      if (metric.dnsResolveDuration != null && metric.dnsResolveDuration! > 500) {
        print('瓶颈:DNS解析耗时过长,建议优化DNS配置');
      } else if (metric.responseReceiveDuration != null && metric.responseReceiveDuration! > 1000) {
        print('瓶颈:响应接收耗时过长,建议优化服务端接口响应速度或减少返回数据量');
      }
    }
  });
}

六、结语:覆盖全流程的网络开发体系

本文提出的“Mock模拟+联调优化+性能监控”方案,聚焦开发、联调、上线前性能校验三大核心阶段的痛点,与此前的“加密、并发控制、异常监控、多环境适配、断点续传、缓存优化、网络自适应”共同构成了覆盖企业级Flutter应用全生命周期的网络开发体系。通过本地+远程双模式Mock,开发者可脱离后端接口独立推进开发;通过调试工具集成,可快速定位联调过程中的问题;通过全链路性能指标采集与分析,可精准优化网络性能瓶颈。

实际开发中,需结合团队协作模式与业务特性灵活调整方案:例如大型团队可搭建统一的远程Mock服务,提升多端协作效率;高频访问的核心接口需重点监控性能指标,确保用户体验;联调阶段可根据需求灵活开启/关闭调试日志,避免敏感信息泄露。通过本文的实战方案,开发者可构建高效、稳定、可调试、可优化的网络交互层,显著提升开发效率与应用质量,为用户提供流畅的网络体验。

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐