在Volley 源码解析及对 Volley 的扩展
系列的第一篇文章中,介绍了一种通过继承 StringRequest
、JsonObjectRequest
等自定义类,只需要重写其中的一个方法,即可获得网络请求的耗时和网络请求的结果,详见第一篇文章。
在这篇文章中将对 Volley 的源码进行解析,只有真正去研究 Volley 的源码之后,才会发现 Volley 设计的真是太精妙了。面向接口编程 在 Volley 中体现的非常彻底。
创建 RequestQueue
请求队列对象
了解 Volley 用法的人都知道,使用 Volley 进行网络请求的第一步就是创建一个请求队列 RequestQueue
对象,创建 RequestQueue
对象的代码如下所示:
RequestQueue mQueue = Volley.newRequestQueue(this);
在 Volley 中是通过静态工厂方法的方式创建 RequestQueue
对象的,Volley
类的源码如下所示:
public class Volley {
/** 默认的文件缓存路径 */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* 创建一个默认的请求队列对象,并调用 {@link RequestQueue#start()} 方法启动它。
*
* @param context 一个 {@link Context} 对象用于创建缓存文件对象
* @param stack 一个用于执行网络请求的 {@link HttpStack} 对象,若为 null,则使用默认的网络请求对象
* @return 一个已经启动的 {@link RequestQueue} 对象
*/
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) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// 在 Android SDK 9 之前,HttpUrlConnection 有 Bug,不可靠,所以使用 HttpClient
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
/**
* 创建一个默认的请求队列对象,并调用 {@link RequestQueue#start()} 方法启动它。
*
* @param context 一个 {@link Context} 对象用于创建缓存文件对象
* @return 一个已经启动的 {@link RequestQueue} 对象
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}
- 在
Volley
类中有两个重载的静态方法
public static RequestQueue newRequestQueue(Context context);
public static RequestQueue newRequestQueue(Context context, HttpStack stack);
第一个方法的实现调用了第二个方法,传入的 HttpStack
对象为 null
- 通过包名和版本号创建
userAgent
对象 - 如果传入的
HttpStack
对象为null
, 则根据 SDK 版本号创建默认的HttpStack
对象,若 SDK 版本号大于等于 9,则创建HurlStack
对象(内部使用HttpUrlConnection
实现);否则创建HttpClientStack
对象(内部使用HttpClient
实现)。 - 通过已经创建的
HttpStack
对象创建一个Network
具体实现类BasicNetwork
的对象 - 通过
BasicNetwork
对象和DiskBasedCache
磁盘缓存对象创建一个RequestQueue
对象,并启动。
创建一个 Request
对象
Request
是代表网络请求的一个抽象类,其中有两个抽象方法,子类必须实现这两个方法:
/**
* 子类必须实现此方法,用于解析网络请求的响应,并返回合适的类型对象。这个方法会在一个
* 工作线程中被调用(即不会在 UI 线程中调用此方法),如果此方法返回 null,则结果并不会被发送。
*
* @param response 来自于网络请求的响应
* @return 解析的结果,如果发生错误则返回 null
*/
abstract protected Response parseNetworkResponse(NetworkResponse response);
/**
* 子类必须实现这个方法,把解析的结果发送给监听器。其中 T 类型的参数 response 要保证
* 不可以为 null,如果解析失败,则解析的结果不会通过此方法发送。
*
* @param response 通过 {@link #parseNetworkResponse(NetworkResponse)} 方法解析的结果
*/
abstract protected void deliverResponse(T response);
- 默认实现
Request
的子类有:StringRequest
、JsonObjectRequest
、
JsonArrayRequest
和ImageRequest
- 我们也可以自定义一个实现
Request
的类,实现上面两个方法,将其加入到网络请求队列中进行网络请求。在下一篇博客中将会举两个自定义Request
的例子 - Volley 中包括 8 种 Http 网络请求方式:
GET
、POST
、PUT
、DELETE
、HEAD
、
OPTIONS
、TRACE
、PATCH
-
Request
类中包含了网络请求的url
,请求方式,请求Header
,请求Body
和请求的优先级等信息。 - 以下三个方法也经常被子类重写
/**
* 返回一个 Map 类型的参数,为这个请求添加网络请求头信息 Http Header。
* 最常用的就是可以把 Cookie 信息通过此方法添加
*/
public Map getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
/**
* 返回一个字节数组的对象作为 POST 或 PUT 请求的 Body 内容。
*
* 当重写此方法时,也需要考虑重写 {@link #getBodyContentType()} 方法
*/
public byte[] getBody() throws AuthFailureError {
Map params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* 在 {@link #getBody()} 没有被重写的情况下,可以通过此方法返回一个
* Map 类型的参数,用于构建 POST 或 PUT 请求方式的 Body 内容
*
* 注意:也可以通过直接重写 {@link #getBody()} 方法自定义 Body 数据。
*/
protected Map getParams() throws AuthFailureError {
return null;
}
RequestQueue 源码分析
RequestQueue
是 Volley 中的核心类,主要用于处理添加进来的网络请求。
在本小节中将会分三部分介绍 RequestQueue
的类,分别是:RequestQueue
中的主要属性、RequestQueue
类的构造方法和 RequestQueue
的主要方法。
RequestQueue 中的主要属性
/**
* 维护了一个等待请求的集合,如果有一个请求正在被处理并且可以被缓存,如果有新的相同
* URL 请求被添加进来以后,则会新的请求则会进入此集合中。此集合主要是为了避免相同的
* 且不必要的网络请求
*/
private final Map>> mWaitingRequests =
new HashMap>>();
/**
* 正在被此 RequestQueue 处理的请求的集合,如果一个请求正在等待被处理或者正在被
* 某个调度线程处理,则它会在此集合中
*/
private final Set> mCurrentRequests = new HashSet>();
/** 缓存请求队列,在此队列中的请求,将通过缓存获取数据 */
private final PriorityBlockingQueue> mCacheQueue =
new PriorityBlockingQueue>();
/** 网络请求队列,在此队列中的请求,将通过网络向服务器发送请求获取数据 */
private final PriorityBlockingQueue> mNetworkQueue =
new PriorityBlockingQueue>();
在 RequestQueue
中有两个 基于优先级 Request
的队列:mCacheQueue
缓存请求队列和 mNetworkQueue
网络请求队列
RequestQueue 的构造方法
/** 默认的网络请求调度线程数量 4 */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache 接口,用户获取和缓存响应结果,默认的实现类是 DiskBasedCache */
private final Cache mCache;
/** Network 接口,用于执行网络请求,默认的实现类是 BasicNetwork */
private final Network mNetwork;
/** 响应分发器,默认的实现类是 ExecutorDelivery */
private final ResponseDelivery mDelivery;
/** 网络调度线程数组,NetworkDispatcher是 {@link Thread} 的子类*/
private NetworkDispatcher[] mDispatchers;
/** 缓存调度线程,是{@link Thread} 的子类*/
private CacheDispatcher mCacheDispatcher;
/**
* 创建工作线程池,不调用 {@link #start()} 方法,就不会开始开始工作,所以创建完请求队列以后,必须调用{@link #start()}
*
* @param cache 向磁盘持久化响应结果的缓存对象
* @param network 执行 Http 请求的对象
* @param threadPoolSize 网络请求调度线程的数量
* @param delivery 一个负责分发响应结果和异常的分发器
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* 调用 {@link #RequestQueue(Cache, Network, int, ResponseDelivery)} 实现
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* 调用 {@link #RequestQueue(Cache, Network, int)} 实现
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
- 有三个构造方法,最终调用的是
RequestQueue(Cache, Network, int,ResponseDelivery)
这个构造方法 - 创建一个
ExecutorDelivery
对象并赋值给mDelivery
,其中在ExecutorDelivery
构造函数中传入的Handler
对象中的Looper
对象是主线程的,这样使用mDelivery
发送的请求响应结果或者异常就被发送到主线程中了 - 创建一个
NetworkDispatcher
类型的数组对象mDispatchers
,默认长度是4 - 可以看到所依赖的属性
mCache
、mNetwork
、mDelivery
都是接口类型的,而不是具体的实现类,这充分体现了面向接口编程的思想
RequestQueue 中的主要方法
还记得在 Volley
类中的 newRequestQueue(Context, HttpStack)
创建完成 RequestQueue
对象 queue
以后,还调用的了 queue.start()
方法,start()
相关方法如下所示:
/**
* 启动在此队列中的线程
*/
public void start() {
stop(); // 在启动之前,需要确保现在正在运行 mCacheDispatcher 线程和 mDispatchers[] 中的线程被终止
// 创建缓存调度线程并启动
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 根据 mDispatchers[] 的长度,创建对应数量的网络调度线程添加进 mDispatchers[] 并启动
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
/**
* 停止缓存调度线程 mCacheDispatcher 和网络调度线程 mDispatchers[]
*/
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
-
NetworkDispatcher
和CacheDispatcher
都是Thread
的子类,都是线程,创建完该对象以后都需要进行调用start()
方法启动该线程。到这块儿的代码,意识到有必要看一下NetworkDispatcher
和CacheDispatcher
这两个类的代码了,先不着急,我们先分析完RequestQueue
的代码。
通过 Volley 进行网络请求时,创建完网络请求之后,需要将网络请求通过 RequestQueue.add(Request)
方法,将网络请求添加进网络请求队列,那么来分析下 add(Request)
方法,这个方法是 RequestQueue
中非常重要的一个方法。
/** 用于为请求生成一个自动增长的序列号 */
private AtomicInteger mSequenceGenerator = new AtomicInteger();
.....
/**
* 得到一个序列号
*/
public int getSequenceNumber() {
return mSequenceGenerator.incrementAndGet();
}
.....
/**
* 向请求队列中添加一个请求
* @param request 向服务器发送的请求
* @return 已经发送经过处理的请求
*/
public Request add(Request request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
// 为请求 request 设置请求队列,并将其添加进 mCurrentRequests 队列中
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
// 根据他们添加进来的顺序设置唯一的序列号
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
// 如果该请求 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.
// 首先根据 cacheKey(其实就是url)判断 mWaitingRequests 中是否有相同的请求正在进行,如果有,则将其添加进 mWaitingRequests 队列中
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
// 如果有相同的请求,则进行入队操作
Queue> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in flight.
// 如果在 mWaitingRequests 中没有相同的请求正在进行,则在 mWaitingRequests 插入一个 null 值
mWaitingRequests.put(cacheKey, null);
// 将该 request 添加进 mCacheQueue 队列中
mCacheQueue.add(request);
}
return request;
}
}
- 通过
add(Request)
添加一个网络请求,首先需要将该请求request
添加到mCurrentRequests
队列中 - 如果该请求不走缓存,则直接将该请求
request
添加到网络请求队列mNetworkQueue
中,结束该方法 - 如果该请求
request
可以走缓存,根据cacheKey
(其实就是url
)判断mWaitingRequests
中是否有相同的请求正在进行,如果有,则将其添加进mWaitingRequests
队列中,如果没有则在mWaitingRequests
中添加值为null
的值,并将其添加进缓存请求队列mCacheQueue
中 -
RequestQueue.add(Request)
方法的流程图(该图出自 Volley 源码解析)如下所示:
RequestQueue
还有一个常用的方法:RequestQueue.cancelAll(Object)
/**
* 一个在 {@link RequestQueue#cancelAll(RequestFilter)} 方法中使用的判断或过滤接口
*/
public interface RequestFilter {
public boolean apply(Request> request);
}
/**
* 将此队列中符合 filter 条件的所有请求取消
* @param filter 使用的过滤条件
*/
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* 通过给定的 tag 取消在此队列中所有 tag 相同的请求,tag绝对不可以为 bull
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request> request) {
return request.getTag() == tag;
}
});
}
- 一般我都会重写每个请求对象
***Request
的setTag()
方法返回一个该请求的TAG
,在合适的地方(比如:Activity.onDestory()
方法中)通过cancelAll(Object)
取消该TAG
对应的请求,以防止发生意外(比如:内存泄露)。
NetworkDispatcher 源码分析
由于 NetworkDispatcher
源码也并不算长,只有100+行,直接上源码,里面的注释也很详细了,后面会配有相应的说明和流程图。
public class NetworkDispatcher extends Thread {
/** 请求服务器的网络请求队列 */
private final BlockingQueue> mQueue;
/** 处理网络请求的实现 Network 接口类的对象 */
private final Network mNetwork;
/** 写缓存的对象 */
private final Cache mCache;
/** 用于发送响应和异常的分发器 */
private final ResponseDelivery mDelivery;
/** 用于标志此线程是否中断 Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* 创建一个网络调度线程,必须调用 {@link #start()} 启动此线程
*
* @param queue 网络请求队列
* @param network 执行网络请求的 Network 接口实现类
* @param cache 将响应写进缓存的 Cache 接口实现类
* @param delivery 用于分发请求结果的分发器
*/
public NetworkDispatcher(BlockingQueue> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
/**
* 强制此调度线程立即停止。如果在队列中仍然有请求,它们不能保证一定会被处理
*/
public void quit() {
mQuit = true;
interrupt();
}
......
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
// 记录网络请求开始的时间
long startTimeMs = SystemClock.elapsedRealtime();
Request> request;
try {
// Take a request from the queue.
// 从队列中取出一个网络请求
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");
// If the request was cancelled already, do not perform the
// network request.
// 如果该请求已经被取消,则不会进行网络请求
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
// 如果服务器返回的 304,并且该请求之前已经得到过并发送过响应结果,则响应结果可以复用,没必要进行新的网络请求,结束本次循环
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
// 在工作线程中解析得到的网络请求响应结果
Response> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
// 根据 request 的 {@link #shouldCache()} 方法判断此请求是否需要进行缓存,如果需要进行缓存处理,则将其放到 mCache 中
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back
// 将响应通过 mDelivery 发送到 UI 线程中
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// 如果抛出 VolleyError 异常,则将网络请求耗时通过{@link volleyError#setNetworkTimeMs(long)}
// 放进 volleyError 中,并通过 mDelivery 将异常发送出去
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
}
catch (Exception e) {
// 若发生其他异常,则生成 VolleyError 对象,并将网络请求耗时放进 volleyError 对象中,
// 并通过 mDelivery 将异常发送出去
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
private void parseAndDeliverNetworkError(Request> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
}
- 在
run()
方法中有一个while(true){ ...... }
代码段,可见启动此线程以后,它就进入一直在循环的状态,不断的从网络请求队列mQueue
中取出网络请求并执行,除非通过quit()
方法改变mQuit
成为true
,该方法才会停止 -
NetworkDispatcher
类中所依赖的属性,mNetwork
、mCache
和mDelivery
都是接口类型的,而不是具体的实现类,这也充分体现了面向接口编程的思想 - 在上面的代码片段中,注释已经很清楚了。下面是一张
NetworkDispatcher
进行网络请求的流程图,出自 Volley 源码解析。
CacheDispatcher 源码分析
public class CacheDispatcher extends Thread {
private static final boolean DEBUG = VolleyLog.DEBUG;
/** 缓存请求队列 The queue of requests coming in for triage. */
private final BlockingQueue> mCacheQueue;
/** 网络请求队列 The queue of requests going out to the network. */
private final BlockingQueue> mNetworkQueue;
/** 缓存接口 The cache to read from. */
private final Cache mCache;
/** 请求结果分发类 For posting responses. */
private final ResponseDelivery mDelivery;
/** 用于标志此线程是否中断 Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* 创建一个 缓存调度线程,必须调用 {@link #start()} 方法,此线程才会开始工作
*
* @param cacheQueue 缓存请求队列
* @param networkQueue 网络请求队列
* @param cache 处理缓存的对象
* @param delivery 分发响应的结果
*/
public CacheDispatcher(
BlockingQueue> cacheQueue, BlockingQueue> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* 强制此线程立即停止,如果在队列中有请求,则不能保证请求一定会被处理
*/
public void quit() {
mQuit = true;
interrupt();
}
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
// 初始化缓存
mCache.initialize();
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
// 从缓存队列中取一个请求,如果没有可用的则阻塞
final Request> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
// 如果缓存请求已经被取消,则不用处理它
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
// 通过 request.getCacheKey() 从缓存中取出对应的缓存记录
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;
}
// If it is completely expired, just send it to the network.
// 如果缓存已经过期,则将该请求添加进网络请求队列中
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
// 如果得到正确的缓存结果,则生成对应的响应
request.addMarker("cache-hit");
Response> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
// 如果缓存记录不需要更新,则直接通过 mDelivery 将结果发送到 UI 线程中
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
// 还存在这样一种情况,缓存记录存在,但是它约定的生存时间已经到了(还未完全过期,
// 叫软过期),可以将其发送到主线程去更新
// 但同时,也要从网络中更新它的数据
request.addMarker("cache-hit-refresh-needed");
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.
// 将其传回主线程的同时,将请求放到Network队列中
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;
}
}
}
}
-
CacheDispatcher
类和NetworkDispatcher
类的代码很相似,都是Thread
的子类,创建该类的对象以后都需要通过start()
方法启动它 -
CacheDispatcher
所依赖的对象mCache
和mDelivery
都是接口类型的,而不是具体的实现类,这也是面向接口编程思想的体现 - 下面是一张
CacheDispatcher
进行缓存请求的流程图,同样出自 Volley 源码解析。
BasicNetwork 源码分析
BasicNetwork
是 Network
接口的实现类,Network
接口是执行网络请求的接口,其中只有一个方法 performRequest(Request)
,该方法由于执行网络请求并返回 NetworkResponse
类型的请求结果,那来看一下在 BasicNetwork
中该方法是怎么实现的
public class BasicNetwork implements Network {
protected final HttpStack mHttpStack;
......
/**
* 通过 {@link HttpStack#performRequest(Request, Map)} 方法执行网络请求,将得到的网络请求响应包装成 NetworkResponse 类型的对象并将其返回
*/
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
// 记录请求开始的时间
long requestStart = SystemClock.elapsedRealtime();
while (true) {
// 请求响应的对象引用
HttpResponse httpResponse = null;
// 请求响应中的 body
byte[] responseContents = null;
// 请求响应中的 header
Map responseHeaders = Collections.emptyMap();
try {
// 请求头
Map headers = new HashMap();
// 将请求中添加的 CacheEntry 添加到请求头中,详见 {@link addCacheHeaders(Map, Cache.Entry)} 方法
addCacheHeaders(headers, request.getCacheEntry());
// 通过 mHttpStack.performRequest(Request, Map) 方法执行具体的网络请求,并得到请求的响应
httpResponse = mHttpStack.performRequest(request, headers);
// 请求响应中的状态行信息对象
StatusLine statusLine = httpResponse.getStatusLine();
// 请求响应中的 状态码
int statusCode = statusLine.getStatusCode();
// 响应中的响应头 Header[] 转换成 Map 的形式
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
// 如果响应中状态码是 304,则表示请求的内容在服务器端没有更改,使用本地的缓存即可
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
// 取出缓存 entry 对象
Entry entry = request.getCacheEntry();
if (entry == null) {
// 如果 entry 对象为 null,则返回一个 entry 为 null 的 NetworkResponse 对象
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
// 即使返回 304 的响应,但是真正的响应中的响应头包括两部分信息:当前返回的响应头和缓存中已经缓存的响应头
entry.responseHeaders.putAll(responseHeaders);
// 使用缓存对象 entry 生成一个 NetworkResponse 对象并返回
// 将请求耗时放入 NetworkResponse 对象中
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.
// 有一些响应(比如:204)并没有 body 内容,所以必须进行检查
if (httpResponse.getEntity() != null) {
// 将响应中的 HttpEntity 对象转换成 byte[] 类型的 responseContents 对象,详见 {@link entityToBytes(HttpEntity)} 方法
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
// 如果响应中的 HttpEntity 为空,也要给 responseContents 对象赋值
responseContents = new byte[0];
}
// if the request is slow, log it.
// 如果请求的耗时太久则打印请求相关的信息,详见 {@link logSlowRequests(long, Request ,byte[], StatusLine)} 方法
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
// 如果响应状态码超出 200-299 的范围,则抛出 IOException 异常
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
// 如果响应状态码在 200-299 之内,则生成 NetworkResponse 对象并返回
// 将请求耗时放入 NetworkResponse 对象中
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
// 处理 SocketTimeout,重复请求
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
// 处理 ConnectTimeout,重复请求
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
// 处理 MalformedURLException,重复请求
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
// 处理 IOException,重复请求
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
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 >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException("server",
request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
......
}
-
BasicNetwork
类中最重要的一个属性就是mHttpStack
是HttpStack
类型的,HttpStack
也是接口类型的,它是具体的执行网络请求的接口,在 Volley 中有两个实现了HttpStack
接口的类:HurlStack
和HttpClientStack
,HurlStack
内部是使用HttpUrlConnection
实现的,而HttpClientStack
内部是使用HttpClient
实现的 - 在
BasicNetwork
中,对响应状态码为304
、204
等特殊情况做了一定的处理,如果状态码在200-299
之外则抛出IOException
,在200-299
之内则生成NetworkResponse
对象并返回,并对各种异常SocketTimeoutException
和ConnectTimeoutException
等异常做了特殊的处理 - 在
performRequest(Request)
中起始的位置记录请求开始的时间,在生成NetworkResponse
对象或者抛出VolleyError
异常中都代码网络请求的时间,这是第一篇博客中请求耗时的原始值
ExecutorDelivery 源码分析
ExecutorDelivery
是 ResponseDelivery
接口的实现类,ResponseDelivery
接口主要有三个方法:
public interface ResponseDelivery {
/**
* 解析一个来自网络或者缓存的响应结果并发送
*/
public void postResponse(Request> request, Response> response);
/**
* 解析一个来自网络或者缓存的响应结果并发送,提供的 Runnable 对象会在发送完结果之后被执行
*/
public void postResponse(Request> request, Response> response, Runnable runnable);
/**
* 向该 request 对象发送一个异常
*/
public void postError(Request> request, VolleyError error);
}
接着看一下 ExecutorDelivery
实现类的代码
public class ExecutorDelivery implements ResponseDelivery {
/** 向主线程中发送结果的线程池对象 */
private final Executor mResponsePoster;
/**
* ExecutorDelivery 的构造方法
* @param handler {@link Handler} 对象决定了是向哪个线程发送结果
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
// 使用提供的 handler 对象实现一个 Executor 对象
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* ExecutorDelivery 的构造方法
* @param executor 用于发送结果的线程池
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@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");
// 根据传进来的 request 、response 和 runnable 对象,生成一个 ResponseDeliveryRunnable 对象,并使用 mResponsePoster 线程池运行
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));
}
/**
* 一个用于将网络请求的响应结果发送到主线程的线程对象
*/
@SuppressWarnings("rawtypes")
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;
}
// Deliver a normal response or error, depending.
// 视情况通过不同的方法发送正确的响应或异常
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 {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
// 如果 mRunnable 对象不为 null, 则执行它
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
- 在
ExecutorDelivery
的构造方法中,需要传入一个Handler
对象,这个Handler
对象是非常重要的。都知道每个Handler
对象都会持有一个Looper
对象,该对象决定了Handler
发送的消息或者任务在那个线程中处理。在分析RequestQueue
源码时,是这样new ExecutorDelivery(new Handler(Looper.getMainLooper()))
生成的ExecutorDelivery
默认的对象,所以默认情况下,通过ExecutorDelivery
对象发送的消息都是在主线程中处理的。这也符合我们的习惯,具体的网络请求结果都是在 UI 线程中直接处理,这样更方便一些 - 在
ExecutorDelivery
中的内部类ResponseDeliveryRunnable
,是非常重要的,在它的run()
方法中,如果成功则调用request
的deliverResponse(T)
方法,否则调用deliverError(VolleyError)
方法。这里的deliverResponse(T)
方法内部最终会回调我们在构建Request
时设置的Response.Listener
对象onResponse(T)
方法的。
至此,关于Volley 源码解析及对 Volley 的扩展
系列的第二篇文章就结束了,从这边文章中也可以知道为什么都说 Volley
具有很强的扩展性,因为很多地方依赖的属性都是接口,而不是具体的实现类。接下来在第三篇文章中就会对 Volley
做一些扩展。如果有什么问题欢迎指出。我的工作邮箱:[email protected]
参考资料:
Volley 源码解析 -- grumoon
Volley学习笔记之简单使用及部分源码详解 -- Yongyu
Volley源码分析【面向接口编程的典范】 -- 王世晖
Volley源码解析<四> RequestQueue请求队列 -- fenggit
Android Volley完全解析(四),带你从源码的角度理解Volley -- 郭霖