转载请附原文链接:Volley学习笔记之简单使用及部分源码详解
一、使用背景简介
现在大多数手机 App 几乎都离不开网络技术,需要手机端与网络服务端进行数据交互,Android 系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection 和 HttpClient,在初学 Android 的时候,这两个类是我们最开始学着使用的,但是在使用过程中需要调取各种API,进行封,然后请求到的结果需要自己去解析,最后再将解析到的数据进行封装存到数据库,整个过程,相当复杂,而且重复性很高,于是针对这种情况,网络上就有大神封装了各种第三方框架供我们使用,将这些复杂冗余操作进行组合优化,使得整个编写过程得以简化,只需简单配置几行代码就可以完成整个流程操作。今天我们介绍的 Volley 就是其中一个优秀第三方框架。笔者所在公司目前项目使用的就是Volley ,所以在使用的同时决定写一系列笔记分析,从学会简单使用,到最后的 Volley 源码分析一系列循序渐进的流程来理解Volley的实现原理。
二、Volley简介
Volley 是 Google在 Google I/O 2013 大会上 推出的 Android 异步网络请求和图片加载框架。
三、简单使用
使用Volley框架实现网络数据请求主要有以下三个步骤:
- 创建RequestQueue对象,定义网络请求队列,RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,避免非常浪费资源的,一般全局使用一个就可以。
- 创建XXXRequest对象(XXXRequest对象可以自己继承Request类进行封装定义,也可以使用Volley已经为我们提供的的StringReqeust、JsonArrayRequest、JsonObjectRequest),这个类主要是功能是传入请求网址、解析返回数据、回调监听返回数据等功能的实现,也是我们经常继承包装的类,在这里可以实现我们想要的返回数据类型。
- 把XXXRequest对象添加到RequestQueue中,开始执行网络请求。
怎么样,这样看来整个网络请求是否是变得便捷,不需要你去考虑,如何调取Http请求各种API,不需要考虑异步等问题,Volley已经帮助我们完成,下面简单以StringReqeust为例子发起一个Get请求,编写一个小的Demo用例,结合代码,加深理解:
网络请求队列一般都是整个APP内使用的全局性对象,因此最好写入Application类中,全局只使用一个,避免浪费资源:
public class MyApplication extends Application {
public static RequestQueue queue;
public void onCreate() {
super.onCreate();
queue = Volley.newRequestQueue(getApplicationContext());
public static RequestQueue getVolleyRequestQueue() {
return queue;
我们还需要修改AndroidManifest.xml文件,使APP的Application对象为我们刚定义的MyApplication,并添加INTERNET权限:
创建StringReqeust对象,并将其添加到RequestQueue中:
* Request.Method.GET 指定请求方法,如果不输入,默认为Get方法
* new Response.Listener 请求成功回调接口
* new Response.ErrorListener() 请求失败回到接口
* @param url 要请求的网址
private void sendRequest(String url) {
StringRequest stringRequest = new StringRequest(Request.Method.GET,url, new Response.Listener() {
public void onResponse(String response) {
Log.d("TAG", response);
}, new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage());
});
MyApplication.getVolleyRequestQueue().add(stringRequest);
好了,这样我们就完成完成了StringRequest请求操作,在onResponse方法内得到我们想要的请求结果String 类型数据。
四、使用思考
在上面使用StringRequest请求过程中,我们只需要简单三步就完成了整个请求,那么有没有想过,Volley内部是如何实现的呢,思考以下几个问题:
- 如何实现高并发请求
- 如何实现异步请求
- 如何实现数据缓存
- 内部是如何各种继承,组合封装最后几行代码就可以实现请求功能,但是使用拓展性又那么强。
相信不想只做代码搬运工的你在使用过程中也会有这些甚至更多疑问,笔者在使用过程中一直好奇这些,否则在项目中只是简单调用人家已经封装好的几行代码,总感觉自己是一个搬运工,不知其所以然,所以现在决定,静下心来,去尝试分析一下Volley源码,以此记录分享。
五、源码分析
首先我们来看看Volley官方给出的一张Volley工作流程图

下面将这张图翻译如下:

通过上面这张图我们可以对Volley工作流程有一个大概的印象,下面我们根据这张流程图以及Volley使用过程来结源码进行分析:
首先从我们使用的入口Volley.newRequestQueue(context)方法来来作为我们分析的切入点,代码如下:
* 使用volley的入口,一般默认调用这个方法即可创建一个默认的网络请求列队,启动一个请求队列RequestQueue,
* 只需要往这个RequestQueue不断 add Request 即可发起请求
* @param context用于创建缓存文件夹
* @return 返回 instance.
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
这个方法仅仅只有一行代码,只是调用了newRequestQueue()的方法重载,并给第二个参数传入null。那我们接着分析带有两个参数的newRequestQueue()方法中的代码,如下所示:
* @param context A 用于创建缓存文件夹
* @param stack HttpStack处理http网络请求包装,可以自己定义,如果传入null,
*那么就使用系统默认的HttpStack
* @return A 返回 instance.
public static RequestQueue newRequestQueue(Context context, HttpStack stack)
return newRequestQueue(context, stack, -1);
这个方法也很简单,直接调用了含三个参数的构造方法,那么我们接着看看含三个参数的构造方法代码:
* @param context A 用于创建缓存文件夹
* @param stack HttpStack处理http网络请求包装,内部就是我
*们使用过的HttpURLConnection或者HttpClient,进行包装处
*理,可以自己定义,也可以传入null,那么就使用系统默认的HttpStack
* @param maxDiskCacheBytes 设置最大sd卡缓存,如果传入-1
*就使用默认缓存
* @return A 返回 instance.
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
* UA一般都用于统计与识别
* User-Agent是Http协议中的一部分,属于头域的组成部
*分,User Agent也简称UA。用较为普通的一点来说
* ,是一种向访问网站提供你所使用的浏览器类型、操作系统
*及版本、CPU 类型、浏览器渲染引擎、浏览器语言、浏览器插
*件等信息的标识。
* UA字符串在每次浏览器 HTTP 请求时发送到服务器
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 {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
Network network = new BasicNetwork(stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
else
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
queue.start();
return queue;
这个方法代码稍微多一点,代码已经注释,这个构造方法主要是new 了一个 负责处理网络请求部分的stack对象,然后 用BasicNetwork将stack进行包装处理,又new了一个负责缓存部分的DiskBasedCache对象,并将这两个参数传入了列队queue中,并启动了列队;这里说明一下,在创建HttpStack对象的时候是比较了一下版本号,如果Build.VERSION.SDK_INT >= 9( API大于9), stack = new HurlStack();这里是因为在Android 2.2版本之前,HttpURLConnection一直存在着一些令人厌烦的bug,在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量。到这里我们就完成使用volley的第一步分析,即获取RequestQueue对象,那么接下来我们分析下RequestQueue构造方法的实现:
* 创建请求列队,传入sd卡缓存包装类,和网络请求包装类
* 会调用含三个参数的构造方法,并传入参数默认参数DEFAULT_NETWORK_THREAD_POOL_SIZE为4,即默认
*线程数为4
* @param cache sd卡缓存类
* @param network 网络请求包装类
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
下面分析含三个参数的构造方法代码:
* 调用含四个参数的构造方法并传递默认 new ExecutorDelivery(new Handler(Looper.getMainLooper()))参数
* @param cache sd卡缓存类
* @param network 网络请求包装类
* @param threadPoolSize 网络请求线程的数量,这里threadPoolSize为DEFAULT_NETWORK_THREAD_POOL_SIZE
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
含四个参数的构造方法代码如下:
* @param cache sd卡缓存类
* @param network 网络请求包装类
* @param threadPoolSize 网络请求线程的数量
* @param delivery用来回调网络请求结果的事件分发类
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
到这里我们来我们发现从调用含有两个参数的RequestQueue构造方法是逐渐调用三个、四个参数构造方法,那么最终实现了:mCachesd卡缓存类的实例化,network网络请求包装类 的实例化,创建了一个数量为4的NetworkDispatcher数组,mDelivery=new ExecutorDelivery(new Handler(Looper.getMainLooper())网络请求结果回调类的实例化。那么这里我们看一下回调网络请求结果类ExecutorDelivery构造方法代码:
创建一个回调网络请求结果的对象
* @param handler 利用handler将runnable发送出去
public ExecutorDelivery(final Handler handler) {
mResponsePoster = new Executor() {
public void execute(Runnable command) {
handler.post(command);
这里的handler为new Handler(Looper.getMainLooper()),即handler是创建在主线程中,所以当调用 handler.post(command)的时候,是将网络请求结果发送到主线程主去处理,这样就完成了异步任务,将子线程中网络请求的结果发送到主线程中去处理。以上就完成了构造RequestQueue对象过程中的代码分析。
下面还有一个疑问要分析就是我们在调用volley的时候执行的RequestQueue.add()方法,分析一下是如何将请求添加到缓存列队和网络请求列队,以及如何进行管理。
* 该方法用于向列队中添加请求request
* 其中包含了几种列队,起到不同的作用
* mCurrentRequests列队,用于存储目前正在进行但是尚未完成的请求
* mNetworkQueue网络请求列队,用于存储走网络的请求
* mWaitingRequests 如果一个请求正在被处理并且可以被缓存,后续的相同 url 的请求,将进入此等待队列
public Request add(Request request) {
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
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 {
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
return request;
下面我们来捋一捋request是如何被添加到请求列队中的步骤如下:
1、当add一个请求request时候,首先该请求会被添加当当前请求列队mCurrentRequests中。
2、如果该请求不使用缓存那么直接被添加到网络请求列队mNetworkQueue结束方法。
3、如果该请求使用了缓存,那么先判断mWaitingRequests列队中是否有该请求,如果有那么添加到mWaitingRequests中,如果没有直接添加到缓存请求列队mCacheQueue中。
到这里我们理解了一个请求是如何被添加到列队中,但是同时会产生一个疑问就是mCurrentRequests和mWaitingRequests这两个列队的作用是什么,以及如何调用的,我们初略猜想一下,这两个列队中维护的请求应该在一个请求结束的时候,将该请求移除,那么应该是RequestQueue的finish方法中进行调用,那么下面我们来看一下代码:
* 该方法会在Request的方法中进行调用finish(String),表明当前请求已经结束
* 需要从mCurrentRequests和mWaitingRequests中移除保存的请求Request
void finish(Request request) {
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
synchronized (mFinishedListeners) {
for (RequestFinishedListener listener : mFinishedListeners) {
listener.onRequestFinished(request);
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
mCacheQueue.addAll(waitingRequests);
上面代码只需要看注释部分即可,并不复杂,那么RequestQueue的finish应该是什么时候调用呢,我们猜想应该是在一个request方法请求结束的时候,我们来看一下在Request类的finish方法中代码是否如此:
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
});
return;
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
上面代码我们在第三行看到了mRequestQueue.finish(this);那么request方法结束请求有三种可能分别是:手动调用request.cancel方法取消请求、request请求成功回调成功结果、request请求失败回调失败结果,这三种请求都是表面当前request请求结束,那么我们分别看一下这三处的代码是否进行了调用,我们以网络请求线程NetworkDispatcher为例(缓存线程调度原理相同):
public void run() {
........
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
........
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
..........
然后看一下请求结果回调类ExecutorDelivery的run,执行了该方法表明将request请求结果进行了回调,所以request.finish()方法在此处也有调用:
public void run() {
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
........
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
.......
这样mCurrentRequests和mWaitingRequests这两个列队的管理基本就捋清楚了。
下面我们就要思考获取到requestQueue对象,调用requestQueue.add(request)方法后,是如何启动网络请求的,那么我们看到在RequestQueue构造方法中有一行代码 queue.start()方法,那么接下来我们来分析一下这个方法具体内容:
* 开始执行在列队中的各个线程分发调度
public void start() {
stop();
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
上面代码主要实现功能是创建一个缓存事件调度线程并启动,然后循环创建了n个网络请求线程调度networkDispatcher对象,这里n= mDispatchers.length。mDispatchers数组的长度即是构造RequestQueue过程中传入的参数DEFAULT_NETWORK_THREAD_POOL_SIZE 即数量为4,也就是说创建了4个负责网络请求调度的线程。下面我们一起分析一下CacheDispatcher缓存调度线程代码,CacheDispatcher 继承自Thread,是一个线程,先看一下构造方法:
* @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;
然后主要看一下run方法:
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mCache.initialize();
while (true) {
try {
final Request request = mCacheQueue.take();
request.addMarker("cache-queue-take");
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
request.addMarker("cache-hit");
Response response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
mDelivery.postResponse(request, response);
} else {
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
mDelivery.postResponse(request, response, new Runnable() {
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
});
} catch (InterruptedException e) {
if (mQuit) {
return;
continue;
这个run方法主要作用是:启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery 去执行,将请求结果发送到主线程中。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。这里有一行代码mDelivery.postResponse(request, response);是将请求结果发送到主线程中,前面我们提到是利用handler.post()方法执行,那么我们看看ExecutorDelivery 类的postResponse的具体细节:
public void postResponse(Request request, Response response) {
postResponse(request, response, null);
就一行代码,调用含三个参数的构造方法,那么我们继续追溯:
public void postResponse(Request request, Response response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
这里的mResponsePoster就是在构造ExecutorDelivery 时候生成的Executor对象,代码如下:
public ExecutorDelivery(final Handler handler) {
mResponsePoster = new Executor() {
public void execute(Runnable command) {
handler.post(command);
而ResponseDeliveryRunnable继承Runnable,所以当调用mResponsePoster.execut(new ResponseDeliveryRunnable(request, response, runnable))方法时候就会将ResponseDeliveryRunnable对象发送到主线程中,那么我们看一下ResponseDeliveryRunnable类的run方法:
("unchecked")
public void run() {
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
if (mRunnable != null) {
mRunnable.run();
这段代码我们看自定义Request时候需要复写的方法 mRequest.deliverResponse(mResponse.result);和mRequest.deliverError(mResponse.error)。到这里请求回调的疑问我们已经解决。
下面我们先分析一下NetworkDispatcher网络缓存线程,NetworkDispatcher 继承自Thread,也就是说NetworkDispatcher 是一个线程先看一下构造方法:
* @param queue 网络请求列队
* @param network 网络请求包装类
* @param cache 缓存类
* @param delivery 请求结果回调分发类
public NetworkDispatcher(BlockingQueue> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
然后看一下复写的run方法:
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
request = null;
try {
request = mQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
continue;
try {
request.addMarker("network-queue-take");
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
addTrafficStatsTag(request);
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
Response response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
request.markDelivered();
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);
run方法启动后会不断从网络请求队列中取请求处理,队列为空则等待,拿到请求后,则执行网络请求,请求处理结束则将结果传递给 ResponseDelivery 去执行后续处理,并判断结果是否要进行缓存。
下面我们来捋一捋volley如如何进行网络请求、缓存请求和请求结果的回调,步骤如下:
1、创建了一个缓存事件调度线程CacheDispatcher,负责从缓存列队中读取请求,然后从缓存中读取数据,进行解析返回,从缓存中读取数据为null,或者数据过期、需要刷新,那么将请求添加到网络请求列队。
2、创建了四个网络请求事件调度线程NetworkDispatcher,负责处理网络请求,并解析请求结果进行返回,并将结果缓存到本地。
3、创建了一个请求结果回调类ResponseDelivery,负责将请求结果返回到主线程,原理是利用handler将结果post到主线程。
到这里volley框架的主要流程我们基本梳理通顺了,下面我们在看一下细节方面,网络请求是如何执行的,上面代码中执行网络请求代码mNetwork.performRequest(request),其中mNetwork就是我们在构造RequestQueue时候传入的BasicNetwork对象,那么看一下具体代码如下:
* 通过HttpStack,即执行http网络请求请求
* 并将HttpStack请求结果包装成NetworkResponse返回NetworkRespons
public NetworkResponse performRequest(Request request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map responseHeaders = Collections.emptyMap();
try {
Map headers = new HashMap();
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);
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
String newUrl = responseHeaders.get("Location");
request.setRedirectUrl(newUrl);
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
responseContents = new byte[0];
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());
...........
这段方法中大多都是一些网络请求细节方面的东西,部分已经添加注释,我们抓重点看即可,其中httpResponse = mHttpStack.performRequest(request, headers);这一行代码中的mHttpStack就是我们在构造RequestQueue的时候传入的HttpStack对象,负责处理http网络请求的,然后就是有几处代码是return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart)),其中区别就是参数不同,所以这个方法其实只要实现功能就是调用HttpStack处理网络请求,并将结果进行包装转换为可被ResponseDelivery处理的NetworkResponse对象返回。
下面我们看一下mHttpStack.performRequest(request, headers)这个方法是如何处理网络请求的(以HurlStack类为例):
* 封装执行HttpURLConnection网络请求,返回HttpResponse对象
* @param request the request to perform
* @param additionalHeaders 附加的请求头文件信息
public HttpResponse performRequest(Request request, Map additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
url = rewritten;
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
setConnectionParametersForRequest(connection, request);
........
response.setEntity(entityFromConnection(connection));
for (Entry> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
return response;
这段代码中我们终于看见了我们熟悉的HttpURLConnection对象,并进行了一系列的参数设置,解析返回数据返回HttpResponse对象。到这里负责执行网络请求部分的内容我们也梳理结束。
至此,关于volley的学习笔记到此结束,如有理解错误地方,还请指教!!
最后其实网络上关 Volley源码分析有很多文章,东西都差不多,但是每个人的侧重点和分析角度以及能否把事情讲的通俗易懂差别就大了,关键是要找到适合自己的,如果感觉本人分析你读着比较懵逼,那可能是我写的不清真,那么可以出门左拐黑丫山上的小帅比 对Volley的深入分析 和郭霖大神的博客去看一下,如果还是看不懂,那就有肯能是你理解的不清真了(just a joke) !!