本篇文章分为两个部分
- Volley的任务取消
- Volley的重试机制
ok,现在就从第一问题开始
一,Volley的任务取消
1, 在网络请求的基类Request.java中
/** Whether or not this request has been canceled. */
private boolean mCanceled = false;
/** An opaque token tagging this request; used for bulk cancellation. */
private Object mTag;
... ...
/**
* Set a tag on this request. Can be used to cancel all requests with this
* tag by {@link RequestQueue#cancelAll(Object)}.
*
* @return This Request object to allow for chaining.
*/
public Request<?> setTag(Object tag) {
mTag = tag;
return this;
}
/**
* Returns this request's tag.
* @see Request#setTag(Object)
*/
public Object getTag() {
return mTag;
}
... ...
/**
* Mark this request as canceled. No callback will be delivered.
*/
public void cancel() {
mCanceled = true;
}
/**
* Returns true if this request has been canceled.
*/
public boolean isCanceled() {
return mCanceled;
}
... ...
- 单一取消请求
在Request中有一个boolean类型变量mCanceled用来标识这个Request请求是否被取消. cancel()方法和 isCanceled()方法分别用来取消和获取是否取消
所以当我们如果某个网络请求时,只需要request.cancel()即可.
不管是在NetworkDispatcher.java中,还是在CacheDispatcher.java还是返回结果的ExecutorDelivery.java中都有关于request请求是否取消的判断
NetworkDispatcher.java
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
... ...
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
... ...
}
CacheDispatcher.java
public void run() {
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
... ...
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
... ...
}
ExecutorDelivery.java
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
... ...
}
由上可知,不管在网络请求的那个阶段(网络请求,缓存请求,结果打回等),都会对请求进行判断是否已经取消
接下来问题了, 上面说的取消单一某个请求直接request.cancel(),那么当我们需要批量取消request的时候怎么办呢?
- 批量取消请求
在最上面贴出的Request.java代码中有private Object mTag变量,用来标识某一类的请求, 可以使用setTag(Object tag) 和getTag().当我们有批量取消请求的业务时,我们可以根据类别给不同的请求设置不同的tag,然后在RequestQueue.java中调用cancelAll()方法即可
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* Cancels all requests in this queue with the given tag. Tag must be non-null
* and equality is by identity.
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}
二,Volley的重试机制
如果未看Volley的代码,让我设计重试机制, 我会考虑在出错时,再次将这个请求添加到网络请求队列再次发起请求.然而,看了Volley的代码,他的设计并不是如此.下面让我们看下他是怎么设计的
与此重试机制相关的有两个重要的类:DefaultRetryPolicy.java和BasicNetwork.java
1,DefaultRetryPolicy.java
用来定义重试机制的策略,例如定义最大尝试次数,最大超时时间,超时时间的乘积因子等.
每次重试,变量mCurrentRetryCount就自增1,达到最大的尝试次数,将抛出异常,结束请求
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
2, BasicNetwork.java
下面然我们看下Volley是怎么实现这个重试机制的
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
... ...
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode = 0;
NetworkResponse networkResponse = null;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(networkResponse);
}
}
}
}
在BasicNetwork.java中的performRequest(Request<?> request)中有一个while(true)死循环, 对的,重试机制就靠这个死循环
- 当网络请求顺风顺水执行完毕,直接返回 NetworkResponse对象.结束当前死循环
- 当网络出现异常时候,将会调用attemptRetryOnException()方法
private static void attemptRetryOnException(String logPrefix, Request<?> request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
attemptRetryOnException()方法会会根据DefaultRetryPolicy对象来判断是否还需要重试,如果不需要,则直接throw error来退出死循环. 如果需要,那么不再抛出异常, 这样由于while(true)死循环就再次执行这个request请求. 避免了开头所说的再次将请求插入到网络访问队列等等繁琐的操作. 他这样设计,简洁,便于处理且减少无所谓的繁琐逻辑
网友评论