99%的开发者都踩过的坑:Dio异常处理完全指南(错误捕获+优雅降级)
【免费下载链接】dio 项目地址: https://gitcode.com/gh_mirrors/dio/dio
你是否遇到过APP突然闪退?用户投诉"加载半天没反应"?接口返回500时页面一片空白?这些问题往往源于异常处理的缺失。Dio作为Flutter生态中最流行的网络请求库,其异常处理机制直接决定了APP的稳定性和用户体验。本文将带你系统掌握Dio异常处理的全流程,从错误捕获到优雅降级,让你的APP从此告别崩溃。
读完本文你将获得:
- 识别8种Dio异常类型的能力
- 3种错误捕获的实战技巧
- 5步实现请求优雅降级
- 完整的异常处理代码模板
Dio异常体系解析
Dio定义了完整的异常类型体系,所有网络错误都会被封装为DioException对象。通过分析dio/lib/src/dio_exception.dart源码,我们可以将异常分为以下8种类型:
| 异常类型 | 触发场景 | 典型案例 |
|---|---|---|
| connectionTimeout | 建立连接超时 | 服务器无响应 |
| sendTimeout | 发送数据超时 | 上传大文件网络慢 |
| receiveTimeout | 接收数据超时 | 下载文件超时 |
| badCertificate | 证书验证失败 | HTTPS证书过期 |
| badResponse | 非预期状态码 | 404/500响应 |
| cancel | 请求被取消 | 用户主动取消 |
| connectionError | 网络连接错误 | 无网络/WiFi断开 |
| unknown | 未知错误 | 序列化失败 |
异常类核心结构
DioException类包含5个关键属性,帮助我们全面定位问题:
class DioException implements Exception {
final RequestOptions requestOptions; // 请求配置
final Response? response; // 响应数据(可能为null)
final DioExceptionType type; // 异常类型
final Object? error; // 原始错误对象
final String? message; // 错误描述
}
异常捕获实战
1. try/catch基础捕获
最直接的异常捕获方式是使用try/catch包裹请求代码:
try {
final response = await dio.get('/api/data');
// 处理正常响应
} on DioException catch (e) {
// 捕获Dio异常
handleDioError(e);
} catch (e) {
// 捕获其他类型异常
print('其他错误: $e');
}
2. 拦截器全局捕获
通过拦截器可以实现全局异常统一处理,避免重复代码:
dio.interceptors.add(InterceptorsWrapper(
onError: (DioException e, handler) {
// 全局异常处理逻辑
logError(e);
showErrorToast(e);
// 根据异常类型决定是否继续传播
if (e.type == DioExceptionType.cancel) {
return handler.reject(e); // 取消请求不提示
}
return handler.next(e);
},
));
3. 取消请求处理
Dio提供了CancelToken机制,用于主动取消请求。在dio/lib/src/cancel_token.dart中定义了取消相关逻辑:
// 创建取消令牌
final cancelToken = CancelToken();
// 发起请求时关联令牌
dio.get('/api/data', cancelToken: cancelToken)
.then((response) {})
.catchError((e) {
if (CancelToken.isCancel(e)) {
print('请求已取消: ${e.message}');
}
});
// 需要取消时调用
cancelToken.cancel('用户主动取消');
异常类型判断与处理
针对不同异常类型,我们需要采取差异化的处理策略。以下是完整的异常处理函数:
void handleDioError(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
showError('网络连接超时,请检查网络');
break;
case DioExceptionType.sendTimeout:
showError('发送数据超时,请稍后重试');
break;
case DioExceptionType.receiveTimeout:
showError('接收数据超时,请检查网络');
break;
case DioExceptionType.badCertificate:
showError('证书验证失败,无法建立安全连接');
break;
case DioExceptionType.badResponse:
handleBadResponse(e.response!);
break;
case DioExceptionType.cancel:
print('请求已取消: ${e.message}');
break;
case DioExceptionType.connectionError:
showError('网络连接错误,请检查网络设置');
break;
case DioExceptionType.unknown:
showError('未知错误: ${e.message ?? e.error}');
break;
}
}
// 处理非预期状态码
void handleBadResponse(Response response) {
switch (response.statusCode) {
case 400:
showError('请求参数错误: ${response.data['message']}');
break;
case 401:
// 处理未授权,通常是token过期
showError('登录已过期,请重新登录');
logout();
break;
case 403:
showError('没有权限访问该资源');
break;
case 404:
showError('请求的资源不存在');
break;
case 500:
showError('服务器内部错误,请稍后重试');
break;
default:
showError('服务器返回异常: ${response.statusCode}');
}
}
优雅降级策略
当网络出现问题时,良好的降级策略能极大提升用户体验。以下是5步实现优雅降级的方案:
1. 请求重试机制
对临时性错误实现自动重试:
Future<T> requestWithRetry<T>({
required Future<T> Function() request,
int maxRetries = 2,
}) async {
int retries = 0;
while (true) {
try {
return await request();
} on DioException catch (e) {
retries++;
if (retries >= maxRetries || !_shouldRetry(e)) {
rethrow;
}
// 指数退避策略
final delay = Duration(milliseconds: 300 * (1 << (retries - 1)));
await Future.delayed(delay);
}
}
}
// 判断是否应该重试
bool _shouldRetry(DioException e) {
return e.type == DioExceptionType.connectionError ||
e.type == DioExceptionType.connectionTimeout ||
(e.type == DioExceptionType.badResponse &&
e.response?.statusCode != null &&
e.response!.statusCode! >= 500);
}
2. 缓存优先策略
使用缓存数据应对网络异常:
class CacheInterceptor extends Interceptor {
final CacheManager cacheManager;
@override
Future<void> onRequest(
RequestOptions options,
RequestInterceptorHandler handler
) async {
// 检查是否有缓存数据
final cacheData = await cacheManager.getCache(options.uri.toString());
if (cacheData != null && options.extra['forceRefresh'] != true) {
// 返回缓存数据
return handler.resolve(Response(
requestOptions: options,
data: cacheData,
statusCode: 200,
));
}
return handler.next(options);
}
// 其他拦截器方法...
}
3. 友好错误提示
根据异常类型提供人性化提示:
void showErrorToast(DioException e) {
String message;
switch (e.type) {
case DioExceptionType.connectionError:
message = '网络连接失败,请检查网络设置';
break;
case DioExceptionType.connectionTimeout:
message = '连接超时,请稍后重试';
break;
case DioExceptionType.receiveTimeout:
message = '数据加载超时,请检查网络';
break;
case DioExceptionType.badResponse:
message = _getResponseErrorMsg(e.response!.statusCode!);
break;
default:
message = '请求失败,请稍后重试';
}
// 显示提示
Fluttertoast.showToast(msg: message);
}
4. 离线数据支持
使用本地数据库存储关键数据,在无网络时提供基础功能:
class OfflineDataRepository {
final Dio dio;
final LocalDatabase db;
Future<List<Item>> getItems() async {
try {
// 优先从网络获取
final response = await dio.get('/items');
final items = (response.data as List).map((e) => Item.fromJson(e)).toList();
// 保存到本地数据库
await db.saveItems(items);
return items;
} on DioException catch (e) {
if (e.type == DioExceptionType.connectionError) {
// 网络错误时返回本地数据
return db.getItems();
}
rethrow;
}
}
}
5. 功能降级开关
通过功能开关控制非核心功能的可用性:
class FeatureManager {
// 根据网络状态决定功能可用性
bool isFeatureAvailable(String feature) {
final networkState = _networkMonitor.currentState;
if (networkState == NetworkState.offline) {
return _offlineAvailableFeatures.contains(feature);
}
return true;
}
}
// UI层使用
if (featureManager.isFeatureAvailable('videoUpload')) {
// 显示视频上传按钮
} else {
// 隐藏或禁用按钮
}
异常监控与分析
完善的异常处理还需要监控系统的支持,以下是一个简单的异常上报实现:
class ErrorReporter {
static void report(DioException e) async {
try {
await dio.post('/api/error-report', data: {
'type': e.type.toString(),
'message': e.message,
'requestUrl': e.requestOptions.uri.toString(),
'requestMethod': e.requestOptions.method,
'statusCode': e.response?.statusCode,
'timestamp': DateTime.now().toIso8601String(),
'deviceInfo': await _getDeviceInfo(),
});
} catch (reportError) {
// 确保上报本身的错误不会影响主流程
print('上报错误失败: $reportError');
}
}
}
完整代码模板
最后,为你提供一个完整的Dio异常处理模板,可直接集成到项目中:
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
class NetworkClient {
final Dio _dio;
NetworkClient() : _dio = Dio() {
_setupDio();
}
void _setupDio() {
// 基础配置
_dio.options.baseUrl = 'https://api.example.com';
_dio.options.connectTimeout = Duration(seconds: 5);
_dio.options.receiveTimeout = Duration(seconds: 3);
// 添加拦截器
_dio.interceptors.add(LogInterceptor(responseBody: true));
_dio.interceptors.add(ErrorInterceptor());
// 配置适配器
_dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
// 配置证书校验等
return client;
},
);
}
// 带重试的GET请求
Future<T> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
int maxRetries = 2,
}) async {
return requestWithRetry(
request: () => _dio.get(
path,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
),
maxRetries: maxRetries,
);
}
// 其他请求方法...
}
// 全局错误拦截器
class ErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
// 错误处理逻辑
_logError(err);
_showErrorToast(err);
_reportError(err);
handler.next(err);
}
void _logError(DioException err) {
// 日志记录
}
void _showErrorToast(DioException err) {
// 显示错误提示
}
void _reportError(DioException err) {
// 错误上报
}
}
// 异常处理工具类
class ExceptionHandler {
static void handle(DioException e) {
// 异常处理逻辑
}
}
总结
Dio异常处理是每个Flutter开发者必备技能,良好的异常处理能显著提升APP质量。本文从异常类型、捕获方式、处理策略到优雅降级,全面覆盖了Dio异常处理的各个方面。记住,稳定的APP不是没有错误,而是能妥善处理每一个可能的错误。
掌握这些技巧后,你可以:
- 精准定位各类网络错误
- 实现全局统一的异常处理
- 为用户提供友好的错误提示
- 在网络异常时保持功能可用
希望本文能帮助你构建更健壮的Flutter应用,让你的APP在各种网络环境下都能提供出色的用户体验!
如果觉得本文对你有帮助,别忘了点赞、收藏、关注三连,下期将为你带来"Dio性能优化实战"!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



