转载请注明出处:http://blog.csdn.net/crazy1235/article/details/71340320
总体流程分析
一般使用Volley的步骤是:
- 创建请求队列
requestQueue = Volley.newRequestQueue(this);
- 创建一个请求
StringRequest stringRequest = new StringRequest(Request.Method.GET, Constants.URL_WEATHER_INFO,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("MainActivity", response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("MainActivity", "error");
}
});
- 将请求添加到请求队列中
requestQueue.add(stringRequest);
在回调函数中得到请求结果
Activity/Fragment销毁时,取消请求
requestQueue.cancelAll(Constants.URL_WEATHER_INFO);
Volley.java
从第一步开始看:
Volley.newRequestQueue(this);
关于RequestQueue(请求队列)的创建,关键代码就在 Volley.java 中!
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
// 1. 创建HttpStack对象
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
// 1. Android 2.3版本及以上
stack = new HurlStack();
} else {
// 2. Android 2.3版本之前,HttpUrlConection 不可用
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
// 2. 创建Network对象 (发起网路请求)
Network network = new BasicNetwork(stack);
// 3. 创建请求队列
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
// 4. 队列启动
queue.start();
return queue;
}
HttpStack 是一个接口,只有一个函数,表示处理网络请求并返回处理结果
public interface HttpStack {
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
它有两个子类 HurlStack & HttpClientStack
Network 也是一个接口,有一个唯一实现子类 BasicNetwork
Network 的作用就是调用HttpStack处理网络请求,并将网络请求结果封装成 NetworkResponse。
下面来看RequestQueue的初始化!
RequestQueue
// 默认线程池大小是4
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
最终都是调用的四个参数的构造,创建了一个长度是4的NetworkDispatcher数组 !
NetworkDispatcher 表示网络分发器,启动之后,不断的从请求队列中取出请求,处理结果交给ResponseDelivery进行分发 (根据请求的情况,成功/失败/异常);如果队列为空,则等待!
关于NetworkDispatcher中的具体操作后面再讲!
ResponseDelivery
ResponseDelivery 是网络请求结果投递接口!有一个实现类 ExecutorDelivery
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
从ExecutorDelivery的构造看出Executor的所有执行都交给handler来处理!
而传入的handler是主线程的handler对象,所以网络请求的结果都投递给主线程来处理了!
看到这里,RequestQueue 对象创建完毕,接着就是调用 start() 函数!
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
首先,调用stop()函数停止正在运行的所有线程!
接着,创建了一个 CacheDispatcher 和 n个 NetworkDispatcher ,并全部启动!
RequestQueue 内部有两个 基于优先级的阻塞队列 :
private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();
private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();
当没有任何Request请求添加进入队列时,CacheDispatcher 和 NetworkDispatcher 虽然已经启动,但是会“空转”,也就是阻塞等待队列中有值!
还有一个正在请求但是未完成 的请求集合!
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
还有另外一个等待请求的集合!
private final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();
OK, 这时Volley请求已经准备完毕,来看看添加一个请求会发生什么!
add(Request<T> request)
/**
* 添加一个请求到请求队列中
*
* @param request The request to service
* @return The passed-in request
*/
public <T> Request<T> add(Request<T> request) {
request.setRequestQueue(this);
// 添加到正在请求的集合中
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// 1. 给请求添加一个序列号
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 2. 如果请求不能被缓存,则直接添加到网络请求队列中
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// 3. 判断等待请求队列中是否由相同key的请求
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey(); // 就是url
// 4. 如果有,则添加到等待请求队列中
if (mWaitingRequests.containsKey(cacheKey)) {
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else { // 5. 如果没有则也添加到等待请求队列中,并将请求添加到 mCacheQueue
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
在这里,一个请求的处理分为三种情况:
如果不允许被缓存,则直接添加到 mNetworkQueue 这个队列中,在NetworkDispatcher 中进行处理!
如果允许被缓存,则进一步判断是否已经有相同的url请求正在进行,如果有,则将当前请求添加到url对应的链表中!(等待正在处理的那个结果回来之后,然后通知所有相同url的请求)
如果没有相同的url正在处理,则添加一条新的记录到 mWaitingRequests 中,并将请求添加到 mCacheQueue 中 ,由CacheDispatcher 进行处理!
处理流程如下:
所以现在只需关注 NetworkDispatcher 和 CacheDispatcher 对 Request 的处理!
CacheDispatcher
在构造RequestQueue的时候,创建了一个 CacheDispatcher 对象,并启动该线程!
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
在构造CacheDispatcher的时候,传进来的参数有:
mCacheQueue – 缓存队列
mNetworkQueue – 网络请求队列
mCache – 缓存对象
mDelivery – 结果分发器
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
// 设置线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 1. 初始化缓存
mCache.initialize();
// 循环
while (true) {
try {
// 2. 从缓存阻塞队列中取request,如果没有的话,会一直阻塞,直到能take到一条请求
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 3. 如果request被取消了,则不在处理
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 4. 尝试从缓存中获取
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 5. 缓存没有命中,则将请求放入网路请求队列中
mNetworkQueue.put(request);
continue;
}
// 6. 如果缓存到期,则添加到网路请求队列中
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// 7. 此时有缓存命中,将data数据交付给request
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 8. 判断是否需要刷新数据
if (!entry.refreshNeeded()) {
// 完全命中cache,直接将结果交付request
mDelivery.postResponse(request, response);
} else {
// Soft-expired 缓存命中,此时可以交付。但是同时需要再次请求以便刷新数据
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// 9. 投递缓存的结果,并将请求添加到mNetworkQueue中以便刷新数据
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}
处理流程图:
首先是缓存的初始化,也就是将缓存的信息读取出来方便匹配request!
对于一个request,我们上面再说 add() 函数的时候提到了三种情况,一般情况下一个request是第三种情况,也就是既添加到缓存队列中,又添加到等待请求队列中!
从上面的流程中可以看出,先把请求从缓存中去匹配,如果匹配到了(不管过期的情况)则通过 ResponseDelivery 投递结果;如果匹配失败再去添加到 mNetworkQueue (网络请求队列)中交给 NetworkDispatcher 去处理!
关于mCache 下面来说!
Cache & DiskBasedCache
CacheDispatcher 的处理离不开 Cache!
Cache 是一个接口
主要方法有:
/**
* 通过key得到缓存的实体对象
*/
public Entry get(String key);
/**
* 保存一个请求的实体对象
*/
public void put(String key, Entry entry);
/**
* 初始化
*/
public void initialize();
/**
* 根据key移除一个缓存实体
*/
public void remove(String key);
/**
* 清除缓存
*/
public void clear();
还有一个静态内部类 Entry
public static class Entry {
/**
* 请求返回的数据
*/
public byte[] data;
/**
* Http响应头里面的实体标签
*/
public String etag;
/**
* Http的响应时间
*/
public long serverDate;
/**
* 缓存的过期时间
*/
public long ttl;
/**
* 缓存的刷新周期
*/
public long softTtl;
/**
* http的相应头部
*/
public Map<String, String> responseHeaders = Collections.emptyMap();
/**
* 判断是否到期
*/
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/**
* 判断是否需要刷新数据
*/
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
有两个实现子类,NoCache就是空实现;DiskBasedCache 就是在Volley.java 初始化RequestQueue的时候传进去的mCache对象!
DiskBasedCache
DiskBasedCache定义了一个 CacheHeader 类,与Cache.Entry类似,表示缓存信息的摘要!
网络请求的缓存写入与读取都是通过CacheHeadler类来操作的!
在 DiskBasedCache 类中定义了一些变量:
/** 缓存CacheHeader的map集合 */
private final Map<String, CacheHeader> mEntries =
new LinkedHashMap<String, CacheHeader>(16, .75f, true);
/** 缓存容量的总大小 */
private long mTotalSize = 0;
/** 缓存的根目录 */
private final File mRootDirectory;
/** 当前缓存的总大小 */
private final int mMaxCacheSizeInBytes;
/** 默认缓存最大容量 */
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
/** 缓存大小的阈值 */
private static final float HYSTERESIS_FACTOR = 0.9f;
/** magic number */
private static final int CACHE_MAGIC = 0x20120504;
initialize() – 初始化
@Override
public synchronized void initialize() {
if (!mRootDirectory.exists()) {
if (!mRootDirectory.mkdirs()) {
VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
}
return;
}
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
CacheHeader entry = CacheHeader.readHeader(fis); // 封装成CacheHeader对象
entry.size = file.length();
putEntry(entry.key, entry);
} catch (IOException e) {
if (file != null) {
file.delete();
}
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ignored) { }
}
}
}
private void putEntry(String key, CacheHeader entry) {
if (!mEntries.containsKey(key)) {
mTotalSize += entry.size;
} else {
CacheHeader oldEntry = mEntries.get(key);
mTotalSize += (entry.size - oldEntry.size);
}
mEntries.put(key, entry);
}
从上面两个函数看出,初始化时,遍历读取缓存目录下的所有缓存文件,组装成一个个的CacheHeader对象! 并且保存到map集合中!
关于文件流的读取,Volley定义了一套加密规则!具体规则就不研究了,但是它保证了写入之后在读取时获取到的就是原值!
public static CacheHeader readHeader(InputStream is) throws IOException {
CacheHeader entry = new CacheHeader();
int magic = readInt(is);
if (magic != CACHE_MAGIC) { // 判断magic
throw new IOException();
}
entry.key = readString(is);
entry.etag = readString(is);
if (entry.etag.equals("")) {
entry.etag = null;
}
entry.serverDate = readLong(is);
entry.ttl = readLong(is);
entry.softTtl = readLong(is);
entry.responseHeaders = readStringStringMap(is);
return entry;
}
put() – 保存缓存
@Override
public synchronized void put(String key, Entry entry) {
pruneIfNeeded(entry.data.length); // 检测是否缓存已满,若时则删除一些缓存!
File file = getFileForKey(key); // 创建缓存文件
try {
FileOutputStream fos = new FileOutputStream(file);
CacheHeader e = new CacheHeader(key, entry); // 构造CacheHeader对象
e.writeHeader(fos); // 写入头信息
fos.write(entry.data); // 写入主体信息
fos.close();
putEntry(key, e); // 保存到map中
return;
} catch (IOException e) {
}
boolean deleted = file.delete(); // ???
if (!deleted) {
VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
}
}
pruneIfNeeded()
private void pruneIfNeeded(int neededSpace) {
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) { // 当前总缓存大小加上要保存的缓存大小不超过最大缓存大小
return;
}
long before = mTotalSize;
int prunedFiles = 0; // 要删除文件的个数
long startTime = SystemClock.elapsedRealtime();
Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
while (iterator.hasNext()) { // 迭代缓存map集合
Map.Entry<String, CacheHeader> entry = iterator.next();
CacheHeader e = entry.getValue();
boolean deleted = getFileForKey(e.key).delete(); // 删除文件
if (deleted) {
mTotalSize -= e.size; // 当前缓存总大小更新
} else {
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
e.key, getFilenameForKey(e.key));
}
iterator.remove();
prunedFiles++; // ++
// 当总大小 + 要缓存文件的大小 不大于 默认总缓存大小的阈值 时 跳出循环,不在删除文件!
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
break;
}
}
if (VolleyLog.DEBUG) {
VolleyLog.v("pruned %d files, %d bytes, %d ms",
prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);
}
}
从上面代码看出,删除文件时总是从map的第一位开始的,当不超过默认总缓存容量阈值的大小时才会停止删除缓存文件!
get() – 获取缓存
@Override
public synchronized Entry get(String key) {
CacheHeader entry = mEntries.get(key); // 从map对象中取
// if the entry does not exist, return.
if (entry == null) {
return null;
}
File file = getFileForKey(key);
CountingInputStream cis = null;
try {
cis = new CountingInputStream(new FileInputStream(file));
CacheHeader.readHeader(cis); // 首先吃掉header信息,因为这些信息entry中已经有了,在initialize()的时候已经读取到了。
byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead)); // 这部分是网路请求的主体信息
return entry.toCacheEntry(data); //将data信息放到 entry中
} catch (IOException e) {
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
remove(key);
return null;
} finally {
if (cis != null) {
try {
cis.close();
} catch (IOException ioe) {
return null;
}
}
}
}
remove() – 移除一个缓存
@Override
public synchronized void remove(String key) {
boolean deleted = getFileForKey(key).delete(); // 删除文件
removeEntry(key); // 从map中删除
if (!deleted) {
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
key, getFilenameForKey(key));
}
}
public File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
// 从这里可以看出,缓存文件的名称是由url地址前半部分的hashcode和后半部分的hashcode拼接而成!
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
private void removeEntry(String key) {
CacheHeader entry = mEntries.get(key);
if (entry != null) {
mTotalSize -= entry.size;
mEntries.remove(key);
}
}
clear() – 清空缓存
@Override
public synchronized void clear() {
File[] files = mRootDirectory.listFiles();
if (files != null) {
for (File file : files) { // 遍历缓存文件夹下的文件,挨个删除
file.delete();
}
}
mEntries.clear(); // map清空
mTotalSize = 0; // 当前缓存总大小置空
VolleyLog.d("Cache cleared.");
}
当一个请求需要被缓存分发器处理以及缓存的相关操作已经介绍完毕,下面来看网络请求分发器!
NetworkDispatcher
每一个 NetworkDispatcher 对象都是一个线程!来看run()函数!
@Override
public void run() {
// 设置线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
// 死循环
while (true) {
try {
// 1. 从请求队列中取出一个请求
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 2. 如果请求被取消,则不在处理
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
// 3. 添加流量统计
addTrafficStatsTag(request);
// 4. 访问网络得到数据组装成NetworkResponse
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 5. 判断服务器是否返回304,如果是则直接读取缓存即可
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 6. 在工作线程上解析 networkResponse
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 7. 将请求结果写入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
request.markDelivered();
// 8. 投递处理结果
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}
处理流程图:
注释写的比较详细了,重点来看下面一行代码:
NetworkResponse networkResponse = mNetwork.performRequest(request);
// ...
Response<?> response = request.parseNetworkResponse(networkResponse);
mNetwork是从Volley类中构造RequestQueue传进来的 BasicNetwork !
而BasicNetwork的构造根据系统的版本传入HurlStack 对象 或者 HttpClientStack 对象!
先来看BasicNetwork是在怎么处理 Request请求的!
BasicNetwork
@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 {
// 1. 添加缓存信息(如果有)的一些头信息
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
// 2. 网络请求结果封装成 HttpResponse 对象
httpResponse = mHttpStack.performRequest(request, headers);
// 3.判断返回码
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// 4. 没有修改,直接将缓存中的data取出做为相应结果
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry() == null ? null : request.getCacheEntry().data,
responseHeaders, true);
}
// 5. 检测是否有相应内容 (比如204状态码没有content)
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
responseContents = new byte[0];
}
// 6. 打印相应比较慢的请求
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
// 7. 状态码不在 200 - 299 之间的抛异常
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
// 8. 返回 NetworkResponse 对象
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError()); // 9.尝试重试
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError()); // 9.尝试重试
} 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));// 9.尝试重试
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(e);
}
}
}
}
不管是那种类型,最后请求的结果都被分装成了 HttpResponse 对象。
HttpResponse对象是 apache包中的类
从上面代码看出,当出现socket连接异常,连接超时异常,IO异常时都会尝试重新请求数据!
其实 performRequest 中是个while(true)循环,当正常返回结果是就跳出了该循环;否则在异常情况下重试还是在循环里面再次请求而已!
下面来看两种HttpStack具体怎么处理得到HttpResponse的!
HttpStack
HrulStack是一个接口,只有一个函数 performRequest()
HurlStack
HurlStack 是通过 HttpURLConnection 方式访问网络
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders()); // 我们通常在这个函数中添加header !!!
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
// 1. 构造HttpURLConnection对象
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
// 2. 添加header信息
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
// 3. 设置参数
setConnectionParametersForRequest(connection, request);
// 4. 判断响应码
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
// 5. 封装成 BasicHttpResponse 对象
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
// 当请求的postbody不为空时使用POST方式,否则使用GET方式
byte[] postBody = request.getPostBody();
if (postBody != null) {
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty(HEADER_CONTENT_TYPE,
request.getPostBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
case Method.GET:
connection.setRequestMethod("GET");
break;
case Method.DELETE:
connection.setRequestMethod("DELETE");
break;
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request); // 添加body参数信息
break;
case Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);// 添加body参数信息
break;
case Method.HEAD:
connection.setRequestMethod("HEAD");
break;
case Method.OPTIONS:
connection.setRequestMethod("OPTIONS");
break;
case Method.TRACE:
connection.setRequestMethod("TRACE");
break;
case Method.PATCH:
addBodyIfExists(connection, request);
connection.setRequestMethod("PATCH");
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams(); //
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
从 getBody() 可以看出,回调的就是 getParams() 函数!也就是我们对request设置参数的函数!
HttpClientStack
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); // 创建HttpGet 或 HttpPost 等请求
addHeaders(httpRequest, additionalHeaders); // 添加header信息
addHeaders(httpRequest, request.getHeaders());// 添加header信息
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000); // 设置连接超时时间
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest); // 执行请求,返回HttpResponse对象
}
从代码看出HttpClientStack的performRequest()函数相对简单!
再回过头来分析,在BasicNetwork类中的performRequest()函数中,通过mHttpStack.performRequest(request, headers) 得到了HttpResponse对象,然后组装成 NetworkResponse 对象返回结果!
再回到NetworkDispatcher中, 看下面的代码:
Response<?> response = request.parseNetworkResponse(networkResponse);
Request是个抽象类!不同的子类parseNetworkResponse()中的处理不尽相同!
下面来看Rquest及其子类的相关处理!
Request
public abstract class Request<T> implements Comparable<Request<T>> {
// 默认编码格式
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
// 支持的请求方式
public interface Method {
int DEPRECATED_GET_OR_POST = -1;
int GET = 0;
int POST = 1;
int PUT = 2;
int DELETE = 3;
int HEAD = 4;
int OPTIONS = 5;
int TRACE = 6;
int PATCH = 7;
}
/**
* Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
* TRACE, and PATCH.
*/
private final int mMethod;
/** 请求URL地址 */
private final String mUrl;
/** 请求错误接口 */
private final Response.ErrorListener mErrorListener;
/** 请求队列 */
private RequestQueue mRequestQueue;
/** Whether or not responses to this request should be cached. */
private boolean mShouldCache = true;
/** Whether or not this request has been canceled. */
private boolean mCanceled = false;
/** Whether or not a response has been delivered for this request yet. */
private boolean mResponseDelivered = false;
// A cheap variant of request tracing used to dump slow requests.
private long mRequestBirthTime = 0;
/** 缓慢请求的阈值 */
private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;
/** 重试策略 */
private RetryPolicy mRetryPolicy;
// 缓存实体类
private Cache.Entry mCacheEntry = null;
/** An opaque token tagging this request; used for bulk cancellation. */
private Object mTag;
/**
* 构造函数
*/
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();
}
// ...省略代码
/**
* 通知请求队列,当前请求已经结束!
*/
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this); // 1.
}
// ...
}
/**
* 标记当前请求被取消
*/
public void cancel() {
mCanceled = true;
}
/**
* 判断是否当前请求被取消(在CacheDispatcher和NetworkDispatcher中都调用了此函数进行判断!)
*/
public boolean isCanceled() {
return mCanceled;
}
/**
* Http请求头 (子类重写此方法可以设置头)
*/
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
/**
* POST/PUT请求时回调此函数设置http参数
*/
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
/**
* 拼接编码params
*/
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
*/
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
/**
* 设置可否被缓存
*/
public final Request<?> setShouldCache(boolean shouldCache) {
mShouldCache = shouldCache;
return this;
}
public final boolean shouldCache() {
return mShouldCache;
}
/**
* 子类必须重写该方法,并且该方法必须运行在工作线程上!
*/
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
/**
* 子类可以重写该方法处理VolleyError
*/
protected VolleyError parseNetworkError(VolleyError volleyError) {
return volleyError;
}
/**
* 子类必须重写此方法用于将处理结果分发给回调函数!
*/
abstract protected void deliverResponse(T response);
/**
* 分发VolleyError给回调函数
*/
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
}
Request有很多个子类:
这里只简单分析StringRequest!
StringRequest
在NetworkDipatcher中
Response<?> response = request.parseNetworkResponse(networkResponse);
如果request是StringRequest对象,则调用该类的parseNetworkResponse()函数!
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
通过 HttpHeaderParser.parseCacheHeaders() 函数将response 解析Cache.Entry对象!
最后封装成 Response 对象!
StringRequest中还重写了父类的deliverResponse()函数,函数内部就是直接调用回调函数的onResponse()方法相应!
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
ResponseDelivery
分析到这,就差一步了。就是对结果进行分发处理,不管是成功的还是失败的结果!
mDelivery.postResponse(request, response);
mDelivery.postError(request, new VolleyError(e));
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
ResponseDelivery 是个接口:
只有一个实现子类!
那么这个子类什么时候创建的呢?
实际上,上面已经提到过,在构造RequestQueue的时候:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
从这三个方法可以看出,不管成功还是失败的处理都调用了 mResponsePoster.execute 。mResponsePoster是个Executor对象!
它执行的对象都交给了主线程的handler进行处理!
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// 1. 判断请求是否被取消,如果被取消则不进行事件处理
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 2. 判断相应是成功还是失败
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// 如果不是立即返回的,(表示从网络读取的)会调用finish()函数结束这个request
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// 执行postRun,(如果有)
if (mRunnable != null) {
mRunnable.run();
}
}
}
OK,这里很明显就是回调了我们设置的成功或者失败的函数!!!
至此,从Volley初始化,到添加一个求Request,到处理一个请求(本地、网络),再到结果处理及分发,最后在主线程接口回调,这一系列过程就分析完毕了!!!
总结
首先是,请求队列的创建,其中根据系统版本创建不同的 HttpStack 对象。接着即是启动队列!
启动队列就是创建一个CacheDispatcher和4个NetworkDispatcher,并全部启动!
然后就可以添加一个request请求进行处理了
添加一个请求时,首先判断是否可以被缓存,不能直接添加到网络请求队列中由NetworkDispatcher处理;如果可以则判断缓存中是否有或者是否已经过期或者需要刷新,进而由NetworkDispatcher 或者CacheDispatcher 处理!
不论是CacheDispatcher还是NetworkDispatcher都讲结果封装成Response 对象!进而由 ResponseDelivery 分发处理!
最后由 ResponseDelivery 回调成功或者失败函数返回结果!
附:
HTTP请求结果状态码!
/**
* An <code>int</code> representing the three digit HTTP Status-Code.
* <ul>
* <li> 1xx: Informational
* <li> 2xx: Success
* <li> 3xx: Redirection
* <li> 4xx: Client Error
* <li> 5xx: Server Error
* </ul>
*/
protected int responseCode = -1;