Google Volley 网络请求框架(一)
Volley是在Google I/O 2013上发布的,说来惭愧,都快一年了,才知道有这么个东西。
有关Volley相关的使用及详细内容,我就不说了。如果还不会用的,但是又想用的可以移步
其它地方。
传送门 (感谢“郭霖”大神)
当然除了大神写的,还可以去看官方文档,也写得相当的好。
虽然看了上述文章,使用起来是完全木有问题。但是问题来了,如果在网络请求的时候出现了bug,
你会如何去解决这个问题呢?
当然,如果你非常了解源码,你肯定知道是怎么引起的。但是我不知道源码啊。肿么破啊。
于是,硬着头皮去看Volley源码,才发现,人家这个比我也得好是有原因的。
先看一下官方给出的架构图嘛,虽然我也不怎么明白是怎么实现的。
当你建立一个Request
,将Request
添加到RequestQuene
中去,Volley 首先会去找Cache里面有没有存在有效的
请求,如果存在,则直接处理里面请求返回的数据并响应回去,如果没有,则把Request
放到网络请求队列里面去,
执行异步请求并响应。
以上就是我暂时对Volley的理解了。
一个完整的网络请求框架,肯定会有请求失败重试的机制。
开始使用的时候,没有注意过请求重试的相关东西。自己也封装过一些网络请求的简单框架。也没有使用过。
最近在使用Volley的时候,它的网络请求是有一个重试机制的,并且你可以自己写重试的条件和次数等。
下面来看一下,Google在Volley是怎么实现的。
先上代码:
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = new HashMap<String, String>();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry() == null ? null : request.getCacheEntry().data,
responseHeaders, true);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} 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);
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);
}
}
}
}
Volley的网络请求是在上述代码中进行执行的。一个while(true)
循环包含了太多东西。
网络请求发出去的了,但是如果网络超时怎么办,当然是重试
呢,可是上述代码是怎么实现重试的呢?
我们可以看到,在catch
里面有两个超时的Exception:
SocketTimeoutException
ConnectTimeoutException
我们在继续往下面看,当它捕获到超时的时候执行了attemptRetryOnException("connection", request, new TimeoutError());
在看一下这个方法的源码:
/**
* Attempts to prepare the request for a retry. If there are no more attempts remaining in the
* request's retry policy, a timeout exception is thrown.
* @param request The request to use.
*/
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));
}
看方法上面的注释就可以清晰的看到,给网络请求建立重试,如果重试的次数已经用完了,则抛出一个timeout exception
.
在来看一下,默认的重试是怎么进行验证的。
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
*/
@Override
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
首先将当前重试的次数加1,如果重试的次数小于等于最大次数,则不管,回到上面那个while(true)
循环继续执行,反之,则抛出异常,最后终止循环。
PS:默认的重试最大次数是1
多了解一下最新的技术,多去看看那些大神写的Blog,对自己的提升会比较大,虽然你不能很好的
去读懂官方给出的英文文档,但是很多技术牛人会将新技术以简单通俗的例子告诉给你。
最后,感谢“郭霖”大神,他的技术博客地址:传送门