在volley中执行网络请求的接口是Network,其实现类是BasicNetwork。
需要注意的是,将Network与HttpStack、HttpClientStack 、HurlStack进行区别。HttpStack是定义网络请求的,是使用HttpClient或者HttpURLConnection来连接网络。Network则是使用HttpStack来执行网络请求。
Network接口内定义了一个唯一的方法public NetworkResponse performRequest(Request<?> request) 其作用就是执行网络请求,并获取返回值。
/**
* An interface for performing requests.
* 调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
*
* 代表网络的接口,处理网络请求。 唯一的方法,用于执行特定请求
*/
public interface Network {
/**
* Performs the specified request. 执行网络请求,获取一个从网络后返回的响应 ,BasicNetwork是该接口的实现类
*
* @param request
* Request to process
* @return A {@link NetworkResponse} with data and caching metadata; will
* never be null NetworkResponse是响应数据,该数据是根据从缓存获得或从网络中获取的响应数据封装起来的
* @throws VolleyError
* on errors
*/
public NetworkResponse performRequest(Request<?> request)
throws VolleyError;
}
Network是一个接口,volley中该接口的默认实现类是BasicNetwork。
BasicNetwork具体执行了网络请求,它有这么几个作用:
1.在构造的时候需要确定使用的网络请求方式,需要HttpStack的实现类,HttpClientStack 或者HurlStack
2.执行HttpStack网络请求,获取响应。
3.请求网络,如果请求失败还需要进行请求重试策略
4.解析响应,组装成可以被volley传递的NetworkResponse
5.在解析响应实体的时候,使用字节数组缓冲池技术
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
//默认的byte[]缓冲池的大小
private static int DEFAULT_POOL_SIZE = 4096;
//具体的执行网络请求的实例,这里使用的是接口,可以用接口的实现类HttpClientStack HurlStack的实例
protected final HttpStack mHttpStack;
//因为大量用到byte[],考虑节省内存问题,创建了一个byte数组缓冲池
protected final ByteArrayPool mPool;
/**
* @param httpStack HTTP stack to be used
* 使用默认大小的字节数组缓冲池
*
*/
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));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations 可以自己来定义一个缓冲池
*/
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mPool = pool;
}
/**
* 执行具体的网络请求 ,需要Volley的Request,返回的是可以被传递的响应
*/
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
//这里使用while(true)的含义是:保证请求重试策略的执行。
//如果网络正常返回结果 那么直接return
//如果需要进行请求重试,就用到这里了,保证了可以进行请求重试
while (true) {
//系统的网络请求的响应
HttpResponse httpResponse = null;
//存放响应实体的字节数组
byte[] responseContents = null;
//存储响应头信息的map
Map<String, String> responseHeaders = new HashMap<String, String>();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
//从request中获取一些头的信息,新鲜度验证的tag和缓存的响应的响应时间
addCacheHeaders(headers, request.getCacheEntry());
//执行请求,获得相应
httpResponse = mHttpStack.performRequest(request, headers);
//获取响应的状态行
StatusLine statusLine = httpResponse.getStatusLine();
//获取状态码
int statusCode = statusLine.getStatusCode();
//将响应的头信息转为map存储
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation. 返回的状态码是304 表示可以使用缓存数据
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) {
//响应实体不为空,使用的是响应实体的数据,获取响应的实体 转为字节数组
//这里有可能抛出IO异常,当出现异常的时候需要再下面捕获异常并进行处理,主要是完成创建可被传递的NetworkResponse
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();
}
//根据httpResponse的信息,组装成可以被volley传递的networkresponse
//此时while循环结束
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
//如果发生超时,认证失败等错误,进行重试操作,直到成功、抛出异常(不满足重试策略等)结束
//当catch后没有执行上边的return 而当前又是一个while(true)循环,可以保证下面的请求重试的执行,是利用循环进行请求重试,请求重试策略只是记录重试的次数、超时 时间等内容。
} catch (SocketTimeoutException e) {
//当出现异常的时候,尝试进行请求重试
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
//当出现异常的时候,尝试进行请求重试
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
//url不正常异常
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
//当出现IO异常时,在try内读取数据体时,如果出现IO异常,那么捕获异常,继续完成创建NetworkResponse的过程
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);
}
}
}
}
/**
* Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
* 如果该请求的时间超过了之前设置的时间,那么log下
*/
private void logSlowRequests(long requestLifetime, Request<?> request,
byte[] responseContents, StatusLine statusLine) {
if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
"[rc=%d], [retryCount=%s]", request, requestLifetime,
responseContents != null ? responseContents.length : "null",
statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
}
}
/**
* 请求重试策略
* 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 {
//内部实现,重试次数+1 超时时间变化
//如果重试次数超过限定的最大次数,该方法抛出异常
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));
}
/**
* 从volley的请求中拿到头信息
* @Description
* @param headers
* @param entry
* @date 2015年5月18日 下午2:32:06
*/
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// If there's no cache entry, we're done. 请求中没有实体,那么不做操作
if (entry == null) {
return;
}
//如果 Request 中带有实体信息,如 Etag,Last-Modify 等,则进行缓存新鲜度的验证
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
//响应产生的时间
if (entry.serverDate > 0) {
Date refTime = new Date(entry.serverDate);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
protected void logError(String what, String url, long start) {
long now = SystemClock.elapsedRealtime();
VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
}
/** Reads the contents of HttpEntity into a byte[]. 将响应的数据实体转为byte[]*/
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
//定义输出流
PoolingByteArrayOutputStream bytes =
new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
byte[] buffer = null;
try {
InputStream in = entity.getContent();
if (in == null) {
throw new ServerError();
}
//从byte[]缓冲池中取字节数组
buffer = mPool.getBuf(1024);
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);//从buffer中读取内容写入输出流中的byte[]中
}
return bytes.toByteArray();
} finally {
try {
// Close the InputStream and release the resources by "consuming the content".
entity.consumeContent();
} catch (IOException e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
VolleyLog.v("Error occured when calling consumingContent");
}
//将字节数组返回到缓冲池中
mPool.returnBuf(buffer);
//关闭输出流
bytes.close();
}
}
/**
* Converts Headers[] to Map<String, String>.
* 将响应的头信息解析出来转为map格式
*/
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;
}
}