Volley框架的基本解读
Posted zero-27
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Volley框架的基本解读相关的知识,希望对你有一定的参考价值。
上一篇Volley框架的基本解读(二)中,我们说到了NetworkDispatcher的run方法,查看源码我们发现,NetworkDispatcher只负责调度并不负责具体的网络请求,它相当于一个中转站,只是将request从RequestQueue手中取来,然后原封不动的交给mNetwork,那么问题来了,mNetwork是什么鬼?我们看源码:
public interface Network
/**
* Performs the specified request.
* @param request Request to process
* @return A @link NetworkResponse with data and caching metadata; will never be null
* @throws VolleyError on errors
*
* 执行request
*/
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
很显然这是一个接口,里面有一个抽象方法,用于执行请求。问题又来了,这个接口的具体实现是什么?什么时候被创建的?让我们回想一下,当我们调用Volley.newRequestQueue方法时,里面是不是有这么一句话:
// 网络接口的基本实现,处理网络请求或失败重试
Network network = new BasicNetwork(stack);
public class BasicNetwork implements Network
public BasicNetwork(HttpStack httpStack)
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
HttpStack正是Volley.newRequestQueue中创建传入的,可见这里又创建了ByteArrayPool缓存池,大小为4096,也就是4KB,然后调用了另一个构造方法:
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool)
mHttpStack = httpStack;
mPool = pool;
这里没什么好说的,我们来看抽象方法performRequest的具体实现:
@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());
// 真正执行网络请求的其实是mHttpStack
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().data, responseHeaders, true);
// Some responses such as 204s do not have content. We must check.
// 可能会发生204无内容返回,因此我们必须检查
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);
代码比较长,大家先别晕,因为这里还只是对网络请求返回数据的封装处理而已,还记得我们在NetworkResponse的run方法中,BasicNetwork调用上面这个方法,返回的是什么吗?答案是NetworkResponse,这个类是一个实体类,封装返回数据:
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified)
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
封装的参数分别是状态码,返回数据,响应头,无修改标记。
知道了这个后,我们再来分析performRequest,里面同样是一个while死循环,在第12行addCacheHeaders方法用于添加缓存请求头,然后mHttpStack调用同名方法进行网络请求,这里mHttpStack就相当于BasicNetwork的代理,返回HttpResponse,接下来大家应该都很熟悉了,得到状态行,再通过状态行得到状态码,调用convertHeaders方法得到响应头:
/**
* Converts Headers[] to Map<String, String>.
*
* 获取响应头
*/
private static Map<String, String> convertHeaders(Header[] headers)
Map<String, String> result = new HashMap<String, String>();
for (int i = 0; i < headers.length; i++)
result.put(headers[i].getName(), headers[i].getValue());
return result;
之后判断状态码是否是304无修改,作出返回,判断状态码是否是204无内容,否读出内容,是给一个0长度的字节数组,logSlowRequests方法不用管,是调试信息,状态码不正确抛出IOException,经过这些判断之后,封装成NetworkResponse给予返回。
流程走通之后,发现也没有那么难,有木有?
上面说的是成功流程,我们再来看看异常流程,该方法一共捕抓了4个异常,SocketTimeoutException与ConnectTimeoutException调用了attemptRetryOnException方法:
/**
* 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.
*
* 尝试重试请求,重试策略由RetryPolicy来决定
*/
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));
细心的读者可能已经发现这个方法会抛出一个VolleyError异常,而这个异常在performRequest方法中并没有捕抓,同样将它抛了出去,还记得我们再说NetworkDispatcher的run时,里面捕抓了一个VolleyError异常吧,正是这里可能出抛出的。
RetryPolicy是一个重试策略类,它由request创建时,在构造方法中被创建:
public Request(int method, String url, Response.ErrorListener listener)
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode();
上面调用了retryPolicy.retry(exception),我们看看DefaultRetryPolicy该类的实现:
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
*
* 这里是重试策略,网络执行BasicNetwork中,网络请求是一个死循环,只有请求成功或抛出异常能够跳出
*/
@Override
public void retry(VolleyError error) throws VolleyError
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
// 当前请求次数超过了最大请求次数,就抛出异常,由BasicNetwork再次抛给NetworkDispatcher,
// 在NetworkDispatcher中做出异常回调
if (!hasAttemptRemaining())
throw error;
/**
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected boolean hasAttemptRemaining()
return mCurrentRetryCount <= mMaxNumRetries;
mCurrentRetryCount是当前的请求次数,默认为0,mCurrentTimeoutMs是当前的超时时间,用于调试之用,我们可以无视,mMaxNumRetries是最大的尝试次数,默认为1。
到这里可能已经有人走通了,如果没有,我们再理理思路,retry方法调起,mCurrentRetryCount变为1,hasAttemptRemaining方法返回为true,BasicNetwork类中performRequest方法中while死循环就无法跳出,继续一次请求,如果再次失败,mCurrentRetryCount变为2,hasAttemptRemaining方法返回为false,抛出异常,attemptRetryOnException方法捕抓后,同样抛出,BasicNetwork再次抛出,由NetworkDispatcher来捕抓该异常,执行错误回调。
这错综复杂的线就通了。
我们接着说,MalformedURLException就是指URL错误,直接抛出RuntimeException。
IOException又分几种详细的错误,判断是否返回HttpResponse,否抛出无连接异常,判断数据是否不为空,否抛出网络异常,是封装NetworkResponse,判断401或403错误,是尝试再次请求,否抛出服务器错误。
这里值得一提的是,封装的NetworkResponse就是服务器返回的错误信息,我们可以通过VolleyError.networkResponse.data拿到它。
好了,我们下一篇博客再见。
以上是关于Volley框架的基本解读的主要内容,如果未能解决你的问题,请参考以下文章