Volley源码解析第二篇
Volley源码解析第一篇: http://blog.youkuaiyun.com/charsion/article/details/52939439
回顾一下,在第一篇中,做了volley网络请求第一步,拿到RequestQueue对象,进入Volley类中,进入到了newRequestQueue(Context context)方法,分析到了HttpStack实例的地方,今天接着上篇继续往下分析,下面进入正题
{
.....
Network network = new BasicNetwork(stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
queue.start();
return queue;
}
- Network
- BasicNetwork
先看看Network 类
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.volley;
/**
* An interface for performing requests.
*/
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
*/
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
是一个接口,注释写的很详细了,执行请求的,里面只有唯一的一个方法performRequest(Request
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.volley.toolbox;
import ...
/**
* A network performing Volley requests over an {@link HttpStack}.
这个**over**该翻译成什么好呢???无非就是network对象执行volley请求需要HttpStack对象(这样就说明了两者之间是有联系的)
*/
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static int DEFAULT_POOL_SIZE = 4096;
protected final HttpStack mHttpStack;
protected final ByteArrayPool mPool; //一个鬼 ByteArrayPool
/**
* @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;
}
//NetworkResponse 第二个鬼(就是存放返回报文信息的一个类)
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
//开始就定义了三个对象,看对象名称就知道这是把返回报文的内容准备好了存放的地方啊
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// Gather headers. //收集请求头信息
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
//下面真正进入主题,发现了两个人之间的秘密了,BasicNetwork原来是在内部调用HurlStack来执行真正的网络请求得到httpResponse ,是不是有种疑问,干嘛不直接就调用HurlStack类,干嘛非要多加一个BasicNetwork,如果说BasicNetwork只是简单的HurlStack包装,那它就真是多余的,但是看到前面那个"鬼"的地方,就该知BasicNetwork没有这么简单
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) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Handle moved resources
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
String newUrl = responseHeaders.get("Location");
request.setRedirectUrl(newUrl);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
//看这里,解决第一个鬼,手哆嗦一下来到entityToBytes方法
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,
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);
}
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
} else {
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 if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
attemptRetryOnException("redirect",
request, new RedirectError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(e);
}
}
}
}
/**
* Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
*/
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 {
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));
}
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// If there's no cache entry, we're done.
if (entry == null) {
return;
}
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.lastModified > 0) {
Date refTime = new Date(entry.lastModified);
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);
}
//在这里用到了mPool,就是前面的那个鬼,你全类搜索,会发现只有这个方法中用到了这个类(除了构造方法里的赋值之外),但是这里又出现了一个新东西PoolingByteArrayOutputStream(妈呀,一个鬼还没有除掉怎么又出来一个新的啊,何时是个头啊,牢骚完了,继续呗,看看PoolingByteArrayOutputStream到底是个啥,进去一看,放心了,只是继承了ByteArrayOutputStream类,无非就是对流操作的类).但是为啥要用到ByteArrayPool类呢,忍不住了,我要去看看ByteArrayPool到底是个啥
/** Reads the contents of HttpEntity into a 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();
}
buffer = mPool.getBuf(1024);
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
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>.
*/
protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}
}
ByteArrayPool
public class ByteArrayPool {
/** The buffer pool, arranged both by last use and by buffer size */
private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
/** The total size of the buffers in the pool */
private int mCurrentSize = 0;
/**
* The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
* under this limit.
*/
private final int mSizeLimit;
/** Compares buffers by size 根据字节数组的大小排序 */
protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {
@Override
public int compare(byte[] lhs, byte[] rhs) {
return lhs.length - rhs.length;
}
};
/**
* @param sizeLimit the maximum size of the pool, in bytes
*/
public ByteArrayPool(int sizeLimit) {
mSizeLimit = sizeLimit;
}
/**
* Returns a buffer from the pool if one is available in the requested size, or allocates a new
* one if a pooled one is not available.
*
* @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
* larger.
* @return a byte[] buffer is always returned.
*/
##### 遍历mBuffersBySize集合,如果找到合适大小的byte[] 数组就返回该byte[]并且移出两个集合中的该元素然后结束循环,如果遍历完集合都没找到就new 一个新的数组
public synchronized byte[] getBuf(int len) {
for (int i = 0; i < mBuffersBySize.size(); i++) {
byte[] buf = mBuffersBySize.get(i);
if (buf.length >= len) {
mCurrentSize -= buf.length;
mBuffersBySize.remove(i);
mBuffersByLastUse.remove(buf);
return buf;
}
}
return new byte[len];
}
/**
* Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
* size.
*
* @param buf the buffer to return to the pool.
*/
##### 刚刚上面的方法是从这个类中拿出东西,用完了总要归还啊
public synchronized void returnBuf(byte[] buf) {
if (buf == null || buf.length > mSizeLimit) {
return;
}
mBuffersByLastUse.add(buf); //体现了先进先出,在后面超过上线的时候,总是首先移出mBuffersByLastUse集合中的第一个元素(也可以理解为最近使用的往往会比较晚被删除)
int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
if (pos < 0) {
pos = -pos - 1;
}
mBuffersBySize.add(pos, buf);
mCurrentSize += buf.length;
trim();
}
/**
* Removes buffers from the pool until it is under its size limit.
*
##### 如果超过了最大的上限,就需要移出mBuffersByLastUse的第一个元素,同时移出mBuffersBySize集合中相同的元素(有点意思)
private synchronized void trim() {
while (mCurrentSize > mSizeLimit) {
byte[] buf = mBuffersByLastUse.remove(0);
mBuffersBySize.remove(buf);
mCurrentSize -= buf.length;
}
}
看完这个类之后,发现它的作用就是保存byte[]字节数组和分配byte[]字节数组,这种思想高啊,跟改善系统内存分配有关啊,只能感叹,未来的路还长啊,还要爬很久啊
总结:
看来这篇文章分析到这里就要结束了,下面屡屡上面都干了啥,首先接着上篇文章的内容继续分析了Volley类,看到了Network network = new BasicNetwork(stack);引入了Network 接口和BasicNetwork的实现类,查看Network 接口发现里面就一个方法 public NetworkResponse performRequest(Request<?> request) throws VolleyError,看着performRequest方法的名字发现跟上篇HurlStack类中的方法名一样,猜测两人之间肯定有点小密码,带着侦探的心情我们来到了Network 接口的实现类,一路小跑终于来到了实现接口方法的地方,往下看了几行代码一下子就找到了两人之间的联系httpResponse = mHttpStack.performRequest(request, headers),其实BasicNetwork内部执行请求的本质还是使用HurlStack对象,往下无非就是为了返回NetworkResponse对象,需要把这个对象的一些属性的内容给设置咯,但是其中特别关注到了一个地方就是设置返回内容的地方,调用了entityToBytes(HttpEntity entity)方法,在该方法内部使用到了PoolingByteArrayOutputStream类ByteArrayPool这个类,研究一番发现PoolingByteArrayOutputStream类就是ByteArrayOutputStream的一个子类,无非就是流的操作了(这句话其实还有一层意思,其实本人对流的操作也不是很清楚,流太多了,这个流那个流的,这也是一个需要学习的地方,不知不觉又发现了一个薄弱点).接下来看看ByteArrayPool类,不得感叹思想真的很重要的,如果是我写代码,这个类对我来说完全可以不存在,但是它为什么存在了呢,它的存在体现了我的渺小,还是感叹一句,编程思想和技巧真的很重要啊.对这个类的一个总结就是对经常需要适用byte[]字节数组的一种优化,其实它真的不难,但是难就难在思想上.
好了,今天就不哔哔哔哔了,如果在阅读过程中发现任何问题,欢迎指出,本人真的就是一枚壳,菜鸟都算不上,乐意接受任何大神的点评,还有如果有跟我一样感觉水平一般,但是又想认真的分析分析一些好的框架,又觉得一个人闷头分析力不从心的,可以联系我啊,我们可以在分析优秀代码之旅上结伴同行.
本文深入剖析了Volley网络库的源码,重点讲解了Network接口及其实现类BasicNetwork的工作原理,包括如何通过HttpStack执行网络请求,以及对响应数据的处理过程。
284

被折叠的 条评论
为什么被折叠?



