彻底搞懂Android Volley错误响应的捕获与分析:从源码到实战的全面解析
一、引言
在Android开发中,网络请求是不可或缺的一部分。而Volley作为Android官方推荐的网络请求库,凭借其简洁的API和高效的性能,受到了广大开发者的喜爱。然而,在实际开发中,网络请求难免会遇到各种错误,如网络连接失败、服务器错误、数据解析异常等。如何正确地捕获和分析这些错误响应,对于提高应用的稳定性和用户体验至关重要。
本文将深入剖析Android Volley库中错误响应的捕获与分析机制,从源码级别详细分析错误处理的各个关键环节。通过本文的学习,你将全面掌握Volley错误响应的捕获方法、错误类型的分类与识别、错误信息的解析与利用,从而在开发中更加高效地处理各种网络错误场景。
二、Volley错误处理概述
2.1 错误处理的基本概念
错误处理是指在程序执行过程中,对可能出现的异常情况进行捕获、分析和处理的过程。在Android开发中,网络请求的错误处理尤为重要,因为网络环境复杂多变,可能会出现各种不可预测的问题。
2.2 Volley错误处理的流程
在Volley中,错误处理的基本流程如下:
- 网络请求发送后,可能会出现各种错误
- Volley将错误信息封装到VolleyError对象中
- VolleyError对象通过ResponseDelivery机制传递到主线程
- 在主线程中,VolleyError对象被传递给我们设置的错误监听器进行处理
- 开发者可以根据错误类型和错误信息,采取相应的处理措施
下面我们将详细分析这个流程中的每一个环节。
三、VolleyError类解析
3.1 VolleyError类的作用
VolleyError类是Volley中用于封装网络请求错误信息的核心类。它继承自Exception类,是所有Volley错误的基类。
3.2 VolleyError类的源码分析
让我们来看一下VolleyError类的源码:
/**
* Volley错误的基类。
*/
public class VolleyError extends Exception {
/** 网络响应数据,如果有的话 */
public final NetworkResponse networkResponse;
/** 网络请求的耗时,单位为毫秒 */
private long networkTimeMs;
/**
* 使用消息创建一个新的VolleyError实例。
*/
public VolleyError() {
networkResponse = null;
}
/**
* 使用消息创建一个新的VolleyError实例。
*/
public VolleyError(String exceptionMessage) {
super(exceptionMessage);
networkResponse = null;
}
/**
* 使用消息和原因创建一个新的VolleyError实例。
*/
public VolleyError(String exceptionMessage, Throwable reason) {
super(exceptionMessage, reason);
networkResponse = null;
}
/**
* 使用原因创建一个新的VolleyError实例。
*/
public VolleyError(Throwable cause) {
super(cause);
networkResponse = null;
}
/**
* 使用网络响应创建一个新的VolleyError实例。
*/
public VolleyError(NetworkResponse response) {
networkResponse = response;
}
/**
* 使用网络响应和原因创建一个新的VolleyError实例。
*/
public VolleyError(NetworkResponse response, Throwable cause) {
super(cause);
networkResponse = response;
}
/**
* 设置网络请求的耗时。
*/
public void setNetworkTimeMs(long networkTimeMs) {
this.networkTimeMs = networkTimeMs;
}
/**
* 获取网络请求的耗时。
*/
public long getNetworkTimeMs() {
return networkTimeMs;
}
}
从上面的源码可以看出,VolleyError类包含了以下几个重要的成员变量:
networkResponse:网络响应数据,如果有的话。它包含了服务器返回的原始数据、HTTP状态码、响应头等信息。networkTimeMs:网络请求的耗时,单位为毫秒。
VolleyError类提供了多个构造函数,允许我们使用不同的参数创建错误实例。例如,我们可以使用消息、原因、网络响应等参数来创建不同类型的错误。
3.3 VolleyError的子类
VolleyError类有多个子类,用于表示不同类型的网络错误。这些子类包括:
- AuthFailureError:表示认证失败错误,通常是由于缺少或无效的认证信息导致的。
- NetworkError:表示网络连接错误,通常是由于网络不可用或连接超时导致的。
- NoConnectionError:表示没有网络连接错误,是NetworkError的子类。
- ParseError:表示解析错误,通常是由于响应数据格式不正确导致的。
- ServerError:表示服务器错误,通常是由于服务器返回了错误的HTTP状态码(如500)导致的。
- TimeoutError:表示请求超时错误,通常是由于请求在规定时间内没有得到响应导致的。
下面我们将详细分析这些子类。
四、AuthFailureError类解析
4.1 AuthFailureError类的作用
AuthFailureError类用于表示认证失败错误,通常是由于缺少或无效的认证信息导致的。
4.2 AuthFailureError类的源码分析
AuthFailureError类的源码如下:
/**
* 表示认证失败的错误。通常是由于缺少或无效的认证信息导致的。
*/
public class AuthFailureError extends VolleyError {
/**
* 创建一个新的AuthFailureError实例。
*/
public AuthFailureError() {
super();
}
/**
* 使用消息创建一个新的AuthFailureError实例。
*/
public AuthFailureError(String message) {
super(message);
}
/**
* 使用网络响应创建一个新的AuthFailureError实例。
*/
public AuthFailureError(NetworkResponse response) {
super(response);
}
/**
* 使用原因创建一个新的AuthFailureError实例。
*/
public AuthFailureError(Throwable cause) {
super(cause);
}
/**
* 获取用于认证的请求头。
* 如果实现了这个方法,Volley会自动在请求中添加这些头。
*/
public Map<String, String> getResponseHeaders() {
return null;
}
/**
* 获取用于认证的请求体。
* 如果实现了这个方法,Volley会自动在请求中添加这个体。
*/
public byte[] getBody() {
return null;
}
}
从上面的源码可以看出,AuthFailureError类继承自VolleyError类,并提供了几个额外的方法:
getResponseHeaders():获取用于认证的请求头。如果实现了这个方法,Volley会自动在请求中添加这些头。getBody():获取用于认证的请求体。如果实现了这个方法,Volley会自动在请求中添加这个体。
4.3 AuthFailureError的使用场景
AuthFailureError通常在以下场景中出现:
- 当请求需要认证信息(如API密钥、OAuth令牌等),但请求中没有包含这些信息时。
- 当提供的认证信息无效或过期时。
- 当服务器要求重新认证时。
在这些情况下,服务器通常会返回401(未授权)或403(禁止访问)HTTP状态码,Volley会将这些响应封装为AuthFailureError。
五、NetworkError类解析
5.1 NetworkError类的作用
NetworkError类用于表示网络连接错误,通常是由于网络不可用或连接超时导致的。
5.2 NetworkError类的源码分析
NetworkError类的源码如下:
/**
* 表示网络连接错误的错误。通常是由于网络不可用或连接超时导致的。
*/
public class NetworkError extends VolleyError {
/**
* 创建一个新的NetworkError实例。
*/
public NetworkError() {
super();
}
/**
* 使用原因创建一个新的NetworkError实例。
*/
public NetworkError(Throwable cause) {
super(cause);
}
/**
* 使用网络响应创建一个新的NetworkError实例。
* 注意:网络响应通常为null,因为在发生网络错误时通常没有响应。
*/
public NetworkError(NetworkResponse networkResponse) {
super(networkResponse);
}
}
从上面的源码可以看出,NetworkError类继承自VolleyError类,并提供了几个构造函数,允许我们使用不同的参数创建错误实例。
5.3 NetworkError的使用场景
NetworkError通常在以下场景中出现:
- 当设备没有网络连接时。
- 当网络连接不稳定或中断时。
- 当服务器不可达时。
- 当DNS解析失败时。
在这些情况下,通常没有服务器响应,因此NetworkError的networkResponse成员变量可能为null。
六、NoConnectionError类解析
6.1 NoConnectionError类的作用
NoConnectionError类用于表示没有网络连接错误,是NetworkError的子类。
6.2 NoConnectionError类的源码分析
NoConnectionError类的源码如下:
/**
* 表示没有网络连接的错误。是NetworkError的子类。
*/
public class NoConnectionError extends NetworkError {
/**
* 创建一个新的NoConnectionError实例。
*/
public NoConnectionError() {
super();
}
/**
* 使用原因创建一个新的NoConnectionError实例。
*/
public NoConnectionError(Throwable reason) {
super(reason);
}
}
从上面的源码可以看出,NoConnectionError类继承自NetworkError类,并提供了几个构造函数,允许我们使用不同的参数创建错误实例。
6.3 NoConnectionError的使用场景
NoConnectionError通常在以下场景中出现:
- 当设备的Wi-Fi和移动数据都关闭时。
- 当设备处于飞行模式时。
- 当设备在没有网络覆盖的区域时。
在这些情况下,应用无法建立任何网络连接,Volley会抛出NoConnectionError。
七、ParseError类解析
7.1 ParseError类的作用
ParseError类用于表示解析错误,通常是由于响应数据格式不正确导致的。
7.2 ParseError类的源码分析
ParseError类的源码如下:
/**
* 表示解析错误的错误。通常是由于响应数据格式不正确导致的。
*/
public class ParseError extends VolleyError {
/**
* 创建一个新的ParseError实例。
*/
public ParseError() {
super();
}
/**
* 使用网络响应创建一个新的ParseError实例。
*/
public ParseError(NetworkResponse response) {
super(response);
}
/**
* 使用原因创建一个新的ParseError实例。
*/
public ParseError(Throwable cause) {
super(cause);
}
}
从上面的源码可以看出,ParseError类继承自VolleyError类,并提供了几个构造函数,允许我们使用不同的参数创建错误实例。
7.3 ParseError的使用场景
ParseError通常在以下场景中出现:
- 当响应数据格式不符合预期时,例如JSON格式不正确。
- 当响应数据使用不支持的编码时。
- 当响应数据不完整或损坏时。
例如,当我们使用JsonObjectRequest请求一个JSON数据,但服务器返回的不是有效的JSON格式时,Volley会抛出ParseError。
八、ServerError类解析
8.1 ServerError类的作用
ServerError类用于表示服务器错误,通常是由于服务器返回了错误的HTTP状态码(如500)导致的。
8.2 ServerError类的源码分析
ServerError类的源码如下:
/**
* 表示服务器错误的错误。通常是由于服务器返回了错误的HTTP状态码(如500)导致的。
*/
public class ServerError extends VolleyError {
/**
* 创建一个新的ServerError实例。
*/
public ServerError() {
super();
}
/**
* 使用网络响应创建一个新的ServerError实例。
*/
public ServerError(NetworkResponse response) {
super(response);
}
/**
* 使用原因创建一个新的ServerError实例。
*/
public ServerError(Throwable cause) {
super(cause);
}
}
从上面的源码可以看出,ServerError类继承自VolleyError类,并提供了几个构造函数,允许我们使用不同的参数创建错误实例。
8.3 ServerError的使用场景
ServerError通常在以下场景中出现:
- 当服务器返回500系列状态码(如500、502、503、504等)时。
- 当服务器遇到内部错误无法处理请求时。
- 当服务器暂时不可用或过载时。
在这些情况下,服务器返回的HTTP状态码表明服务器端存在问题,Volley会将这些响应封装为ServerError。
九、TimeoutError类解析
9.1 TimeoutError类的作用
TimeoutError类用于表示请求超时错误,通常是由于请求在规定时间内没有得到响应导致的。
9.2 TimeoutError类的源码分析
TimeoutError类的源码如下:
/**
* 表示请求超时的错误。通常是由于请求在规定时间内没有得到响应导致的。
*/
public class TimeoutError extends VolleyError {
/**
* 创建一个新的TimeoutError实例。
*/
public TimeoutError() {
super();
}
/**
* 使用原因创建一个新的TimeoutError实例。
*/
public TimeoutError(Throwable cause) {
super(cause);
}
}
从上面的源码可以看出,TimeoutError类继承自VolleyError类,并提供了几个构造函数,允许我们使用不同的参数创建错误实例。
9.3 TimeoutError的使用场景
TimeoutError通常在以下场景中出现:
- 当网络连接缓慢,请求在超时时间内没有得到响应时。
- 当服务器负载过高,无法及时处理请求时。
- 当请求的数据量过大,传输时间超过超时时间时。
在Volley中,我们可以通过setRetryPolicy方法设置请求的超时时间和重试策略。如果请求在指定的超时时间内没有得到响应,Volley会抛出TimeoutError。
十、错误响应的捕获过程
10.1 网络请求执行过程中的错误捕获
在Volley中,网络请求的执行主要由NetworkDispatcher类负责。当执行网络请求时,会捕获各种可能的异常,并将其转换为相应的VolleyError。
下面是NetworkDispatcher类的run方法的部分源码,展示了错误捕获的过程:
/**
* NetworkDispatcher的run方法
*/
@Override
public void run() {
// 设置线程名称
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 循环处理请求
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 从请求队列中取出一个请求
request = mQueue.take();
} catch (InterruptedException e) {
// 如果线程被中断,退出循环
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 如果请求已取消,跳过处理
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
// 标记请求开始网络处理
addTrafficStatsTag(request);
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服务器返回304状态码(未修改),并且我们有缓存条目,则使用缓存响应
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 解析网络响应
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果请求需要缓存,将响应写入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 标记请求已交付
request.markDelivered();
// 将响应传递到主线程
mDelivery.postResponse(request, response);
} catch (SocketTimeoutException e) {
// 处理超时异常
attemptRetryOnException("socket", request, new TimeoutError());
} catch (MalformedURLException e) {
// 处理URL格式错误
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
// 处理IO异常
int statusCode = 0;
NetworkResponse networkResponse = null;
if (connection != null) {
statusCode = connection.getResponseCode();
} else {
throw new NoConnectionError(e);
}
// 读取错误响应
inputStream = connection.getErrorStream();
try {
byte[] errorResponse = entityToBytes(inputStream, connection.getContentLength());
networkResponse = new NetworkResponse(statusCode, errorResponse,
convertHeaders(connection.getHeaderFields()), false,
SystemClock.elapsedRealtime() - requestStart);
} catch (IOException ioe) {
VolleyLog.e("Error reading error response body: %s", ioe.getMessage());
}
// 处理可重试的错误
if (statusCode == HttpURLConnection.HTTP_UNAVAILABLE ||
(request.shouldRetryServerErrors() && isRetriableStatusCode(statusCode))) {
attemptRetryOnException("server", request, new ServerError(networkResponse));
}
// 抛出其他错误
if (networkResponse != null) {
throw new ServerError(networkResponse);
} else {
throw new NetworkError(e);
}
} catch (VolleyError volleyError) {
// 处理网络错误
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
// 处理其他异常
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
从上面的代码可以看出,在执行网络请求的过程中,会捕获以下几种异常:
- SocketTimeoutException:当请求超时时,捕获该异常,并调用
attemptRetryOnException方法尝试重试请求,或者将其转换为TimeoutError。 - MalformedURLException:当URL格式错误时,捕获该异常,并抛出RuntimeException。
- IOException:当发生IO异常时,捕获该异常,并根据具体情况将其转换为NoConnectionError或ServerError。
- VolleyError:当发生Volley错误时,捕获该异常,并调用
parseAndDeliverNetworkError方法处理错误。 - Exception:当发生其他异常时,捕获该异常,并将其转换为VolleyError。
10.2 响应解析过程中的错误捕获
在响应解析过程中,也会捕获各种可能的异常,并将其转换为相应的VolleyError。例如,在JsonObjectRequest的parseNetworkResponse方法中:
/**
* JsonObjectRequest将响应数据解析为JSONObject
*/
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
// 将响应数据转换为字符串
String jsonString = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
// 将字符串解析为JSONObject
return Response.success(
new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
// 处理编码异常
return Response.error(new ParseError(e));
} catch (JSONException je) {
// 处理JSON解析异常
return Response.error(new ParseError(je));
}
}
从上面的代码可以看出,在解析JSON数据时,会捕获以下两种异常:
- UnsupportedEncodingException:当响应数据使用不支持的编码时,捕获该异常,并将其转换为ParseError。
- JSONException:当响应数据不是有效的JSON格式时,捕获该异常,并将其转换为ParseError。
十一、错误响应的传递过程
11.1 通过ResponseDelivery传递错误
在Volley中,错误响应通过ResponseDelivery接口传递到主线程。ResponseDelivery接口定义了将响应和错误传递到主线程的方法。
下面是ResponseDelivery接口的源码:
/**
* 响应分发接口,负责将响应和错误传递到主线程。
*/
public interface ResponseDelivery {
/**
* 将响应传递到主线程
*
* @param request 原始请求
* @param response 响应数据
*/
void postResponse(Request<?> request, Response<?> response);
/**
* 将响应传递到主线程,并在传递完成后执行回调
*
* @param request 原始请求
* @param response 响应数据
* @param runnable 传递完成后执行的回调
*/
void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* 将错误传递到主线程
*
* @param request 原始请求
* @param error 错误信息
*/
void postError(Request<?> request, VolleyError error);
}
Volley提供了一个默认的ResponseDelivery实现类ExecutorDelivery,它使用一个Executor将响应和错误传递到主线程。
11.2 ExecutorDelivery类的实现
ExecutorDelivery类的源码如下:
/**
* 默认的响应分发器实现,使用Executor将响应和错误传递到主线程。
*/
public class ExecutorDelivery implements ResponseDelivery {
/** 用于在主线程执行的Executor */
private final Executor mResponsePoster;
/**
* 创建一个新的ExecutorDelivery实例
*
* @param handler 用于在主线程执行的Handler
*/
public ExecutorDelivery(final Handler handler) {
// 创建一个在主线程执行的Executor
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* 创建一个新的ExecutorDelivery实例,使用提供的Executor
*
* @param executor 用于执行的Executor
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
// 标记请求为已完成
request.markDelivered();
request.addMarker("post-response");
// 创建一个响应分发Runnable
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
// 添加错误标记
request.addMarker("post-error");
// 解析错误响应
Response<?> response = Response.error(error);
// 创建一个响应分发Runnable
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
/**
* 用于在主线程分发响应的Runnable
*/
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// 如果请求已取消,不进行分发
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 处理成功响应
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
// 处理错误响应
mRequest.deliverError(mResponse.error);
}
// 如果响应是中间响应(例如,用于流式响应)
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
// 完成请求
mRequest.finish("done");
}
// 如果有回调Runnable,执行它
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
从上面的代码可以看出,ExecutorDelivery类的postError方法会创建一个包含错误信息的Response对象,并通过mResponsePoster将其传递到主线程。在主线程中,ResponseDeliveryRunnable的run方法会调用request.deliverError(mResponse.error),将错误信息传递给我们设置的错误监听器。
十二、错误响应的分析与处理
12.1 错误类型的判断与识别
在处理Volley错误时,我们首先需要判断错误的类型,以便采取相应的处理措施。可以通过 instanceof 运算符来判断错误的具体类型。
下面是一个示例代码,展示了如何判断错误类型:
// 创建请求时设置错误监听器
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// 处理成功响应
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// 判断错误类型并进行相应处理
if (error instanceof TimeoutError || error instanceof NoConnectionError) {
// 处理超时或无连接错误
showToast("网络连接超时,请检查网络设置");
} else if (error instanceof AuthFailureError) {
// 处理认证失败错误
showToast("认证失败,请重新登录");
// 跳转到登录页面
navigateToLoginPage();
} else if (error instanceof ServerError) {
// 处理服务器错误
showToast("服务器错误,请稍后再试");
} else if (error instanceof NetworkError) {
// 处理网络错误
showToast("网络连接错误,请检查网络设置");
} else if (error instanceof ParseError) {
// 处理解析错误
showToast("数据解析错误,请稍后再试");
}
}
});
12.2 错误信息的获取与分析
VolleyError类提供了一些方法,用于获取错误的详细信息,帮助我们分析和定位问题。
下面是一些常用的方法:
- getMessage():获取错误消息。
- getNetworkTimeMs():获取网络请求的耗时。
- getCause():获取错误的根本原因。
- getNetworkResponse():获取网络响应数据,如果有的话。
下面是一个示例代码,展示了如何获取和分析错误信息:
// 创建请求时设置错误监听器
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// 处理成功响应
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// 获取错误信息
String errorMessage = error.getMessage();
long networkTimeMs = error.getNetworkTimeMs();
Throwable cause = error.getCause();
// 记录错误信息
Log.e(TAG, "请求错误: " + errorMessage);
Log.e(TAG, "请求耗时: " + networkTimeMs + "ms");
// 如果有网络响应,获取响应状态码和数据
if (error.networkResponse != null) {
int statusCode = error.networkResponse.statusCode;
byte[] data = error.networkResponse.data;
Log.e(TAG, "HTTP状态码: " + statusCode);
// 如果有响应数据,尝试将其转换为字符串
if (data != null && data.length > 0) {
try {
String responseData = new String(data, "UTF-8");
Log.e(TAG, "响应数据: " + responseData);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "无法解析响应数据", e);
}
}
}
// 如果有根本原因,记录根本原因
if (cause != null) {
Log.e(TAG, "错误原因: " + cause.getMessage(), cause);
}
// 根据错误类型进行相应处理
if (error instanceof TimeoutError) {
// 处理超时错误
showToast("请求超时,请重试");
} else if (error instanceof ServerError) {
// 处理服务器错误
showToast("服务器错误: " + error.networkResponse.statusCode);
} else {
// 处理其他错误
showToast("请求失败: " + errorMessage);
}
}
});
12.3 自定义错误处理
除了使用Volley提供的错误类型,我们还可以自定义错误处理逻辑。例如,我们可以创建一个基类错误监听器,统一处理常见的错误情况:
/**
* 自定义错误监听器基类
*/
public abstract class BaseErrorListener implements Response.ErrorListener {
private Context mContext;
public BaseErrorListener(Context context) {
mContext = context;
}
@Override
public void onErrorResponse(VolleyError error) {
// 记录错误信息
Log.e("VolleyError", "请求错误: " + error.getMessage());
// 获取错误类型并进行相应处理
if (error instanceof TimeoutError) {
handleTimeoutError();
} else if (error instanceof NoConnectionError) {
handleNoConnectionError();
} else if (error instanceof AuthFailureError) {
handleAuthFailureError();
} else if (error instanceof ServerError) {
handleServerError(error);
} else if (error instanceof NetworkError) {
handleNetworkError();
} else if (error instanceof ParseError) {
handleParseError();
} else {
handleOtherError(error);
}
// 调用抽象方法,让子类处理特定错误
onSpecificError(error);
}
/**
* 处理超时错误
*/
protected void handleTimeoutError() {
showToast("请求超时,请检查网络连接");
}
/**
* 处理无连接错误
*/
protected void handleNoConnectionError() {
showToast("没有网络连接,请检查网络设置");
}
/**
* 处理认证失败错误
*/
protected void handleAuthFailureError() {
showToast("认证失败,请重新登录");
// 跳转到登录页面
navigateToLoginPage();
}
/**
* 处理服务器错误
*/
protected void handleServerError(VolleyError error) {
if (error.networkResponse != null) {
int statusCode = error.networkResponse.statusCode;
showToast("服务器错误: " + statusCode);
} else {
showToast("服务器错误,请稍后再试");
}
}
/**
* 处理网络错误
*/
protected void handleNetworkError() {
showToast("网络连接错误,请检查网络设置");
}
/**
* 处理解析错误
*/
protected void handleParseError() {
showToast("数据解析错误,请稍后再试");
}
/**
* 处理其他错误
*/
protected void handleOtherError(VolleyError error) {
showToast("请求失败: " + error.getMessage());
}
/**
* 显示Toast消息
*/
private void showToast(String message) {
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
/**
* 跳转到登录页面
*/
private void navigateToLoginPage() {
// 实现跳转到登录页面的逻辑
Intent intent = new Intent(mContext, LoginActivity.class);
mContext.startActivity(intent);
}
/**
* 让子类处理特定错误
*/
protected abstract void onSpecificError(VolleyError error);
}
然后在创建请求时,可以使用这个基类错误监听器:
// 创建请求时使用自定义错误监听器
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// 处理成功响应
}
},
new BaseErrorListener(context) {
@Override
protected void onSpecificError(VolleyError error) {
// 处理特定于这个请求的错误
}
});
这样,我们就可以统一处理常见的错误情况,同时让子类处理特定于某个请求的错误。