Flutter网络请求进阶:Mock模拟、联调优化与性能监控实战
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_log、flutter_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服务,提升多端协作效率;高频访问的核心接口需重点监控性能指标,确保用户体验;联调阶段可根据需求灵活开启/关闭调试日志,避免敏感信息泄露。通过本文的实战方案,开发者可构建高效、稳定、可调试、可优化的网络交互层,显著提升开发效率与应用质量,为用户提供流畅的网络体验。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐



所有评论(0)