Android Volley源码解析
Posted 路过你的全世界
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Volley源码解析相关的知识,希望对你有一定的参考价值。
写在前面
最近在整理之前写的项目,当时开发的时候对于Volley没有深入的研究过,这次拿出来进行下源码分析。虽然Volley框架目前已经有些过时,但是里面的思想对于个人来说还是很有成长价值。
整体过程描述
我们使用Volley进行联网获取接口数据的整体流程是怎样的呢?我们很有必要在深入代码细节讲解之前从整体上把握下,这样将有助于我们更好的理解volley的源码
首先,我们要使用volley进行网络访问获取数据,那么volley就需要先做一些初始化的工作:
创建缓存文件“volley”,并设置最大为5M的大小。
根据我们手机当前版本,选择合适的网络操作类(httpClient、httpUrlConnection)进行网络操作类的一系列的封装和初始化。
- 开启一个缓存线程和四个网络线程,对任务请求队列一直进行监听,当有任务到来的时候,它们就会按照逻辑执行。对于缓存线程而言,它需要初始化缓存文件夹volley,将当前文件夹中的缓存加载到内存;对于网络线程而言,它还构建了从主线程到子线程通讯的通道(Handler)。
以上就是volley的初始化做的主要工作了。之后,我们肯定要实例化一个request类,将这个request添加到上述讲到的任务等待队列进行执行。那么后续就是缓存线程和网络线程之间的操作了:
首先判断该请求是否设置了缓存选项(默认为应该缓存),如果设置了不缓存,那么该请求被放置到网络访问等待执行序列。如果是可以进行缓存,那再看是否是第一次发出,如果是第一次发出,那么volley将会对该请求缓存。
如果1中的request属于不缓存的request,那么在网络线程将会在网络访问等待执行序列中取出request进行执行,那么就可以得到网络访问之后的结果。其结果通过Handler提供给request抽象类的实现类,例如StringRequest,我们就可以在UI线程中获取到网络执行之后的结果了;
如果1中的request属于可缓存:
- 但是是第一次访问的情况的话,那么在缓存线程会将该request直接加入到网络访问等待执行序列。之后得到结果之后首先进行缓存,之后通过handler返回结果给UI线程。
- 如果request不是第一次访问,那么要检查缓存中保存的该request是否过期,是否“新鲜”;如果已经过期或者不“新鲜”,那么会将该request直接加入到网络访问等待执行序列;反之的话,将使用缓存中保存的数据返回给UI线程。
文字也许看着比较累,下面我们使用图例来展示下Volley的初始化和整体流程
Volley源码细节分析
初始调用
首先对于Volley的使用大家应该比较熟悉了,我们分析源码就从Volley暴露出来的接口开始即可,下面是一个很简单的Volley请求代码:
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
StringRequest stringRequest = new StringRequest("https://www.baidu.com",
new Response.Listener<String>()
@Override
public void onResponse(String response)
Log.d("TAG", response);
, new Response.ErrorListener()
@Override
public void onErrorResponse(VolleyError error)
Log.e("TAG", error.getMessage(), error);
);
requestQueue.add(stringRequest);
暴露的Volley类
下面让我们看下Volley具体的执行过程:
对于第一行代码而言,最重要的就是volley的newRequestQueue方法了,我们看下源码:
/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
public static RequestQueue newRequestQueue(Context context)
return newRequestQueue(context, null);
public static RequestQueue newRequestQueue(Context context, HttpStack stack)
//初始化名字为“Volley”的缓存文件
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)
//初始化网络访问类
if (stack == null)
if (Build.VERSION.SDK_INT >= 9)
//当前SDK版本大于9,则内部由封装HttpUrlConnection类的HurlStack实现
stack = new HurlStack();
else
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
//低版本SDK,则由封装HttpClient类的HttpClientStack实现
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
//进一步封装网络请求类,得到BasicNetwork类实例,该类实例用于进行处理网络访问
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
HurlStack && HttpClientStack介绍
上述根据SDK版本不同而进行区分使用的实质在于使用网络访问类的不同,即HttpClient类与HttpUrlConnection类使用差别,本文不展开讲述,可自行查阅。
DiskBasedCache类介绍
下面我们查看第35行的源码,首先是DiskBasedCache构造函数:
/** Default maximum disk usage in bytes. */
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
public DiskBasedCache(File rootDirectory)
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes)
mRootDirectory = rootDirectory;
mMaxCacheSizeInBytes = maxCacheSizeInBytes;
由上可知:缓存文件最大可支持5M的大小,并且实质上DiskBasedCache类就是Volley中唯一实现Cache接口的缓存类
//根据URL得到缓存文件夹中的entry数据
@Override
public synchronized Entry get(String key)
CacheHeader entry = mEntries.get(key);
// if the entry does not exist, return.
if (entry == null)
return null;
File file = getFileForKey(key);
CountingInputStream cis = null;
try
cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
CacheHeader.readHeader(cis); // eat header
byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
return entry.toCacheEntry(data);
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;
//将本地缓存文件加载到内存
@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)
BufferedInputStream fis = null;
try
fis = new BufferedInputStream(new FileInputStream(file));
CacheHeader entry = CacheHeader.readHeader(fis);
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)
//向缓存中写入数据(根据URL创建缓存文件,将entry实体进行保存)
@Override
public synchronized void put(String key, Entry entry)
pruneIfNeeded(entry.data.length);
File file = getFileForKey(key);
try
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
CacheHeader e = new CacheHeader(key, entry);
boolean success = e.writeHeader(fos);
if (!success)
fos.close();
VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
throw new IOException();
fos.write(entry.data);
fos.close();
putEntry(key, e);
return;
catch (IOException e)
boolean deleted = file.delete();
if (!deleted)
VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
//根据缓存key值进行缓存子文件名称定义:
//1.对url前半段的串进行hash计算
//2.对url后半段的串进行hash计算
//cacheFileName = 1+2的值
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;
/**
* Returns a file object for the given cache key.
*/
public File getFileForKey(String key)
return new File(mRootDirectory, getFilenameForKey(key));
RequestQueue类介绍
接下来要讲的就是RequestQueue类和他的start方法了:
//设定网络线程个数
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
//存放request的缓存型优先堵塞队列
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
//存放request的网络执行型优先堵塞队列
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
public RequestQueue(Cache cache, Network network)
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
public RequestQueue(Cache cache, Network network, int threadPoolSize)
//ExecutorDelivery类实现了ResponseDelivery接口
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;
public void start()
stop(); // Make sure any currently running dispatchers are stopped.
// 实例化缓存线程,并执行
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 实例化4个网络线程并开始执行
for (int i = 0; i < mDispatchers.length; i++)
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
RequestQueue这个类除了start方法比较重要,另外一个重要的地方在哪里呢?我们还记得在activity中最后需要把具体的request加入到RequestQueue中吧?让我们来看下RequestQueue类的add方法实现
//Request任务等待序列,存储的是具有相同url请求地址的Request对象
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
//当前正在执行的Request序列集合
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
//缓存线程的核心处理数据源:Request缓存优先阻塞队列
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
//网络线程的核心处理数据源:Request的缓存优先阻塞队列
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
public <T> Request<T> add(Request<T> request)
// Tag the request as belonging to this queue and add it to the set of current requests.
request.setRequestQueue(this);
synchronized (mCurrentRequests)
//将新加进来的request添加到当前任务集合
mCurrentRequests.add(request);
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 如果request要求不进行缓存,
//则直接将该request加入"网络型"优先阻塞队列
if (!request.shouldCache())
mNetworkQueue.add(request);
return request;
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests)
String cacheKey = request.getCacheKey();
//对于正在执行的request的时候又加入了同样的request请求的话
//会将后续的request添加到等待序列,直到当前request执行完毕
//执行完毕之后,会调用finish方法,将该类request清空
if (mWaitingRequests.containsKey(cacheKey))
// 属于同一类request,则统一放在一个队列里(LinkedList)
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
// 当前等待序列并没有返现有执行该类request
//则将该request加入等待序列,并加入到“缓存型”优先阻塞队列,等待缓存线程来执行。
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
return request;
<T> void finish(Request<T> request)
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests)
//将执行完毕的request在当前执行request集合中清除
mCurrentRequests.remove(request);
synchronized (mFinishedListeners)
for (RequestFinishedListener<T> listener : mFinishedListeners)
listener.onRequestFinished(request);
if (request.shouldCache())
synchronized (mWaitingRequests)
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null)
if (VolleyLog.DEBUG)
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
add方法思路:
1. 查看request是否需要被缓存,如果不需要则直接交给网络优先阻塞队列去执行
2. 对于多个相同request的请求的操作处理:
- 将加进来的request放在一个hashSet的集合中,该set集合表示肯定要执行的request
接着request会被加到一个waitingRequest,类型为hashMap。key为request的url,value为linkedList,linkedlist保存相同key的request。
以上保证了相同的request不会执行多次,只是会执行一次。
当前一个request执行完毕,会将set集合中的request该实例删除,并将根据url删除waitingRequest中对应的value值。最后将linkedList中的所有request添加进入缓存阻塞队列
ExecutorDelivery类介绍
在上述代码中比较好理解,我们先来看下ExecutorDelivery类,ExecutorDelivery类是一个很重要的类,它主要实现了缓存线程与UI线程切换与信息发送、网络线程与UI线程切换与信息发送的功能:
public class ExecutorDelivery implements ResponseDelivery
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster;
//ExecutorDelivery构造方法之一,主要完成子线程与UI线程的切换
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);
;
/**
* Creates a new response delivery interface, mockable version
* for testing.
* @param executor For running delivery tasks
*/
public ExecutorDelivery(Executor executor)
mResponsePoster = executor;
//以下Override标识的三个方法,为ResponseDelivery接口的实现方法
@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));
/**
* A Runnable used for delivering network responses to a listener on the
* main thread.
*/
@SuppressWarnings("rawtypes")
//该Runnable对象实质上已经是被切换到UI线程执行
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()
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled())
mRequest.finish("canceled-at-delivery");
return;
// 将调用Request类的具体实现类方法deliverResponse,将结果返回到最上层
if (mResponse.isSuccess())
mRequest.deliverResponse(mResponse.result);
else
mRequest.deliverError(mResponse.error);
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate)
mRequest.addMarker("intermediate-response");
else
//普通request请求将在这里被清除出当前任务执行序列和任务等待序列
//Request.finish最后调用RequestQueue的finish方法
//具体查看上述RequestQueue中finish源码
mRequest.finish("done");
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null)
mRunnable.run();
上述代码的核心也就是使用handler进行线程的切换了,如果对于handler机制不太了解,可以查看我的这篇handler的博文:Android消息机制讲解
需要指出的是第73和第75行代码中执行的deliverResponse()和deliverError()方法。实质上这两个方法都是抽象类Request中的方法:
//具体实现Request抽象类的request实现该方法(例如StringRequest)
abstract protected void deliverResponse(T response);
public void deliverError(VolleyError error)
if (mErrorListener != null)
mErrorListener.onErrorResponse(error);
随便查找一个实现Request抽象类的实体类,比如StringRequest,它的deliverResponse()实现如下:
@Override
protected void deliverResponse(String response)
mListener.onResponse(response);
到了这里,是不是感觉代码变得有点眼熟?我们在创建一个StringRequest实例的时候,构造方法实现中就使用到了mListener和mErrorListener。这样我们就可以得到我们发送request之后得到的结果了。
介绍完ExecutorDelivery类,再让我们回到RequestQueue类的start方法上:接下来的关注核心就是两个类型的线程了:即缓存线程CacheDispatcher类和网络线程NetworkDispatcher类,两个类都继承自Thread类,下面我们依次进行源码剖析:
CacheDispatcher类介绍
CacheDispatcher继承了Thread类,主要工作就是加载本地缓存文件,将UI线程请求的Request的情况进行筛选比较,来决定Request的走向:可以是将请求加入到网络执行等待队列去执行、也可以是拿出缓存中该Request的应答response直接返回UI线程等等…我们接下来看他的run方法的实现(已经去掉不重要的部分代码):
@Override
public void run()
//设置当前线程级别:后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 将本地的缓存文件加载到内存
mCache.initialize();
while (true)
try
// 一直从“缓存型”优先阻塞队列中获取请求
final Request<?> request = mCacheQueue.take();
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled())
//如果执行过程中取消该request,则将request在当前执行序列中除去并且在等待序列中除去具有相同URL的队列
//(在讲解完毕网络线程源码之后会详细介绍)
request.finish("cache-discard-canceled");
continue;
//先检查本地缓存,如果本地缓存中没有对应的已经封装好response的Entry类
//则将该request加入“网络型”优先阻塞队列
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null)
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
// 如果本地有缓存,但是已经过期,则将该request加入“网络型”优先阻塞队列
//(可以结合下面的网络线程保存response缓存一起看)
if (entry.isExpired())
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
// 根据本地缓存生成一个response回应实体类
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
if (!entry.refreshNeeded())
// 是否需要重新刷新数据,不需要则将该缓存生成的response返回给UI线程
mDelivery.postResponse(request, response);
else
//需要重新刷新数据
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(request, response, new Runnable()
@Override
public void run()
try
//将该request加入到“网络型”优先阻塞队列
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;
以上就是缓存线程的run方法的全部实现了,该方法的涉及到的技术点总结如下:
基于阻塞队列实现的“生产者”与“消费者”线程模型
基于Http缓存机制实现的判断本地缓存是否过期等问题
以上这两个知识点是理解缓存线程实现机制的前提条件,由于篇幅有限,本文不再过多涉及这两个方面知识点,请读者自行查阅。 了解Http缓存机制的知识可以点击这里
以上CacheDispatcher类的源码已经基本剖析完毕,接下来我们看下NetworkDispatcher类的具体源码
NetworkDispatcher类介绍
NetworkDispatcher类也是继承自Thread,同样最重要的方法就是里面的run方法了:
@Override
public void run()
//设置当前线程为后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true)
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try
// 从阻塞队列中拿出一个请求,如果队列为空,则阻塞等待
request = mQueue.take();
catch (InterruptedException e)
if (mQuit)
return;
continue;
try
if (request.isCanceled())
//请求被取消,在当前执行序列和等待序列中删除该request
request.finish("network-discard-cancelled");
continue;
addTrafficStatsTag(request);
// 根据请求得到networkResponse对象
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if (networkResponse.notModified && request.hasHadResponseDelivered())
//返回304响应,并且之前已经处理过该请求,则略过
request.finish("not-modified");
continue;
// 根据networkResponse中数据生成response.cacheEntry,并且返回response实例
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
//将request和对应的response相应保存到本地缓存
if (request.shouldCache() && response.cacheEntry != null)
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
// 标记该request已经被处理
request.markDelivered();
//调用ExecutorDelivery.postResponse方法,相应源码在上面已经贴出
//即将处理结果返回给UI线程
mDelivery.postResponse(request, response);
catch (VolleyError volleyError)
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
catch (Exception e)
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
NetworkDispatcher类的主要功能如上,已经在代码中较为详细的做了解释说明。需要指出的是第28行代码中mNetwork.performRequest方法和第37行的request.parseNetworkResponse方法,下面我们依次来分析下他们的源码。mNetwork是BasicNetwork类的实例,那么我们先来看下BasicNetwork的源码实现:
BasicNetwork源码介绍
BasicNetwork类是NetWork接口的唯一实现,主要用来进行网络操作的封装和网络响应的处理(各种返回码的判断)。核心方法就是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 = Collections.emptyMap();
try
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
//将该请求加上"If-None-Match"、"If-Modified-Since"
//此处属于进行http的对比缓存策略
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
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);
//如果是304响应,而且request保留着entry缓存,
//则直接使用本地缓存构建一个NetworkResponse返回
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null)
//将httpResponse值写入
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();
//一般的新请求从这里进行返回NetworkResponse实例
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);
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
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
else
throw new NetworkError(networkResponse);
Request抽象类
Request类是一个抽象类,实现它的子类主要要实现parseNetworkResponse抽象方法。我们这里使用的时StringRequest类,那么我们看下StringRequest的实现:
@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));
上述代码比较简单,根据response的编码方式对response数据进行编码并返回String类型的数据源。之后调用了Response.success方法,第二个形参执行了HttpHeaderParser.parseCacheHeaders函数:
public static Cache.Entry parseCacheHeaders(NetworkResponse response)
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0;
long lastModified = 0;
long serverExpires = 0;
long softExpire = 0;
long finalExpire = 0;
long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag = null;
String headerValue;
headerValue = headers.get("Date");
if (headerValue != null)
serverDate = parseDateAsEpoch(headerValue);
headerValue = headers.get("Cache-Control");
if (headerValue != null)
hasCacheControl = true;
String[] tokens = headerValue.split(",");
for (int i = 0; i < tokens.length; i++)
String token = tokens[i].trim();
if (token.equals("no-cache") || token.equals("no-store"))
mustRevalidate = true;
else if (token.startsWith("max-age="))
try
maxAge = Long.parseLong(token.substring(8));
catch (Exception e)
else if (token.startsWith("stale-while-revalidate="))
try
staleWhileRevalidate = Long.parseLong(token.substring(23));
catch (Exception e)
else if (token.equals("must-revalidate") || token.equals("proxy-revalidate"))
mustRevalidate = true;
headerValue = headers.get("Expires");
if (headerValue != null)
serverExpires = parseDateAsEpoch(headerValue);
headerValue = headers.get("Last-Modified");
if (headerValue != null)
lastModified = parseDateAsEpoch(headerValue);
serverEtag = headers.get("ETag");
// Cache-Control takes precedence over an Expires header, even if both exist and Expires
// is more restrictive.
if (hasCacheControl)
softExpire = now + maxAge * 1000;
finalExpire = mustRevalidate
? softExpire
: softExpire + staleWhileRevalidate * 1000;
else if (serverDate > 0 && serverExpires >= serverDate)
// Default semantic for Expire header in HTTP specification is softExpire.
softExpire = now + (serverExpires - serverDate);
finalExpire = softExpire;
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = finalExpire;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
return entry;
看到上面的代码我们是不是就豁然开朗了?这个方法执行的就是将response中所有的重要数据进行转换生成Volley自己的缓存对象Entry的过程:包括Http缓存字段以及请求应答Data在内的所有数据都进行了保存。如果对Http缓存策略还是比较陌生的话可以查看这篇博文:彻底弄懂HTTP缓存机制及原理
以上,基本上完成了Volley框架的核心模块的介绍,包含:Volley的网络线程执行原理、缓存线程执行原理、本地缓存情况介绍、http网络缓存的处理(先强制缓存策略后对比缓存策略)等等。
总结
以上源码可能一时看上去似懂非懂,那么我
以上是关于Android Volley源码解析的主要内容,如果未能解决你的问题,请参考以下文章