Volley为我们提供了五种基本的网络请求方式,分别是StringRequest、JsonRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest。下面分别介绍这五种请求的用法。
一、Volley框架用法
首先当然是创建RequestQueue对象,然后使用该对象把请求对象添加进去。先说下,在用完Request和RequestQueue后一定要在onDestroy()中调用stop(),因为从你创建RequestQueue对象的那一刻起,Volley就默认的给你开启了五个子线程,如果你没有添加Request到队列,那么线程就一直等待,等待,等待。。。即使用户按下返回键,Activity也是不会被finish掉的,因为子线程们还是在等待,这时会造成严重的内存泄露(如果用户多次退出和进入那个Activity,会创建多个Activity实例)。务必注意。
下面介绍Volley的五种基本用法:
1、StringRequest用法:
/**
* StringRequest请求
*
* @return 返回StringRequest对象
*/
public StringRequest startStringRequest() {
StringRequest jora = new StringRequest(mUrl,
new Response.Listener() {
@Override
public void onResponse(String arg0) {
// 注意,在这个方法中处理请求返回来的正确结果,根据自己的业务需求处理,下同
if (mResponceCallback != null) {
mResponceCallback.onStringParse(arg0);
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// 这里处理请求返回的错误,下同
VolleyLog.e("Error: ", error.getMessage());
}
});
if (mRequestQueue != null) {
mRequestQueue.add(jora);
}
return jora;
}
2、JsonObjectRequest用法
/** JsonObject请求 */
public JsonObjectRequest startJsonObjectRequest() {
JsonObjectRequest jora = new JsonObjectRequest(mUrl, null,
new Response.Listener() {
@Override
public void onResponse(JSONObject arg0) {
if (mResponceCallback != null) {
mResponceCallback.onJsonObjectPrase(arg0);
} else {
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
Log.i("TAG", arg0.getMessage());
}
});
if (mRequestQueue != null) {
mRequestQueue.add(jora);
}
return jora;
}
3、JsonArrayRequest用法
/**
* JsonArrayRequest请求
*
* @return 返回JsonArrayRequest对象
*/
public JsonArrayRequest startJsonArrayRequest() {
JsonArrayRequest jsona = new JsonArrayRequest(mUrl,
new Response.Listener() {
@Override
public void onResponse(JSONArray response) {
if (mResponceCallback != null) {
mResponceCallback.onJsonArrayParse(response);
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
Log.i("TAG", arg0.getMessage());
}
});
if (mRequestQueue != null) {
mRequestQueue.add(jsona);
}
return jsona;
}
4、ImageRequest用法
/**
* 图片请求方法
*
* @param imageUrl
* 图片地址
* @param width
* 宽度
* @param height
* 高度
* @param config
* 图片质量配置,取Bitmap.Config类的四个值
* @param errorListener
* 错误监听
* @return
*/
public ImageRequest startImageRequest(String imageUrl, int width,
int height, Bitmap.Config config,
Response.ErrorListener errorListener) {
ImageRequest imageRequest;
imageRequest = new ImageRequest(imageUrl,
new Response.Listener() {
@Override
public void onResponse(Bitmap response) {
if (mResponceCallback != null) {
mResponceCallback.onImageSet(response);
}
}
}, width, height, config, errorListener);
if (mRequestQueue != null) {
mRequestQueue.add(imageRequest);
}
return imageRequest;
}
当然了,对于图片的请求,Volley还提供了一种处理方式,如下:
// 获取NetworkImageView实例
mNetImag = (NetworkImageView) findViewById(R.id.net_image);
// 创建ImageLoader对象
mImageLoader = new ImageLoader(mRequestQueue, new ImageCache() {
@Override
public void putBitmap(String url, Bitmap bitmap) {
// 在这里将请求到的图片做缓存处理
Log.d(TAG, "url--->" + url + "-->bitmap:" + bitmap);
}
@Override
public Bitmap getBitmap(String url) {
//
return null;
}
});
// 设置请求错误时的图片
mNetImag.setErrorImageResId(R.drawable.ic_launcher);
// 设置默认的图片
mNetImag.setDefaultImageResId(R.drawable.ic_launcher);
// 为图片设置url,ok,收工
mNetImag.setImageUrl(mImageUrl, mImageLoader);
除了基本用法以外,Volley还提供了高度定制、灵活的用法,比如在请求还未完成时你可以随时取消任务,也可以间接的修改连接时长等,下面就介绍一些使用Volley的小技巧:
-------------------------------------------------------------------------华丽分隔线--------------------------------------------------------------------------
1、自定义连接时长
在Request类中有一个setRetryPolicy()方法用来设置RetryPolicy,通常我们创建一个请求对象时可能并没有用到,实际上Volley会默认的给我们设置这个属性,不信我们看下StringRequest的构造方法:
/**
* Creates a new request with the given method.
*
* @param method the request {@link Method} to use
* @param url URL to fetch the string at
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(int method, String url, Listener listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
不过貌似没有看到设置那个方法啊,但是他有一个super()调用了其父类也就是Request()的构造方法,我们去Request()中去看看:
/**
* Creates a new request with the given method (one of the values from {@link Method}),
* URL, and error listener. Note that the normal response listener is not provided here as
* delivery of responses is provided by subclasses, who have a better idea of how to deliver
* an already-parsed response.
*/
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
// 给了一个默认的RetryPolicy
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode();
}
正如上所示,我们在创建相应的请求时,Volley为我们创建了默认的RetryPolicy,RetryPolicy只是一个接口,就这个名字翻译过来是“政策重定义”,也就是我们队Request的一些行为重新规范。RetryPolicy接口有一个实现类DefaultRetryPolicy,那我们再去那个类看看:
/**
* Default retry policy for requests.
*/
public class DefaultRetryPolicy implements RetryPolicy {
/** The current timeout in milliseconds. */
private int mCurrentTimeoutMs;
/** The current retry count. */
private int mCurrentRetryCount;
/** The maximum number of attempts. */
private final int mMaxNumRetries;
/** The backoff multiplier for for the policy. */
private final float mBackoffMultiplier;
/** The default socket timeout in milliseconds */
/** 默认连接超时时长为2.5s */
public static final int DEFAULT_TIMEOUT_MS = 2500;
/** The default number of retries */
public static final int DEFAULT_MAX_RETRIES = 1;
/** The default backoff multiplier */
public static final float DEFAULT_BACKOFF_MULT = 1f;
/**
* Constructs a new retry policy using the default timeouts.
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
/**
* Constructs a new retry policy.
* @param initialTimeoutMs The initial timeout for the policy.
* @param maxNumRetries The maximum number of retries.
* @param backoffMultiplier Backoff multiplier for the policy.
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
/**
* Returns the current timeout.
*/
@Override
public int getCurrentTimeout() {
return mCurrentTimeoutMs;
}
/**
* Returns the current retry count.
*/
@Override
public int getCurrentRetryCount() {
return mCurrentRetryCount;
}
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
*/
@Override
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}
二、Volley框架源码分析 首先来了解下Volley的原理:我们在使用时首先创建一个RequestQueue对象,该对象表示一个请求队列,然后我们创建相应的请求,并加入到该请求队列中。然后调用start()方法即可开始网络请求。
我们已经知道Volley的基本用法了,那么现在已经迫不及待的想去一窥源码吧!源码并不多,算上相关的Log、Error、缓存等类也就只有四五十个类,核心类也就十几个,下面先把主要的核心类介绍一下:
(1)Volley:这个类很简单,对于开发者来说就是获取RequestQueue对象,而实际上,在获取对象时也帮我们的应用程序创建好了缓存目录、根据系统版本使用不同的网络请求方式等。
(2)RequestQueue:Volley框架的“首脑”,它会负责将用户的请求添加到子线程去执行,是它将用户的Request、Network、CacheDispatcher和NetworkDispatcher衔接起来,配合完成任务的。
(3)Request:是一个抽象类,它的直接实现子类有ClearCacheRequest、ImageRequest、JsonRequest、StringRequest,同时JsonRequest也是一个抽象类,它有两个实现子类分别是JsonObjectRequest和JsonArrayRequest。顾名思义Request就是用户的请求,通常开发人员根据自己的需求使用不同的请求。
(4)HttpStack:一个接口,他有两个实现类,分别是HttpClientStack和HurlStack,前者实现了HttpClient请求方式,后者实现了HttpURLConnect请求方式,我们都知道,HttpClient是Apache提供的开源的网络请求方式,而HttpURLConnection是JDK提供的标准的网络请求方式,实质上都是HTTP请求,实际上,Android的Framework在HttpClient的基础上自己实现了一个AndroidHttpClient类,在Volley框架中就使用到了它。Volley框架会根据系统版本选择不同的请求方式,2.2版本以下会使用HttpClient请求,在2.2版本以上会使用HttpUrlConnection请求。
(5)Network:是一个接口,它有一个BasicNetwork实现类,它负责将用户的Request使用HttpStack封装好的请求在子线程中去执行。实际上,我们在使用Volley类创建RequestQueue对象时,框架就已经帮我们创建好了HttpStack了,并且将HttpStack对象传递给Network,然后将Network对象和缓存目录传递给RequestQueue,从而创建一个RequestQueue对象,而如果你不想使用Volley类提供的方式来获取RequestQueue,那你要做的事情就多了,RequestQueue开放了三个构造函数,都包含了一个Network参数,因此,你需要实现一个Network类,当然你也可以直接使用BasicNetwork,但是BasicNetwork构造函数又需要提供一个HttpStack参数或者直接使用它的两个实现类,因此,你如果想要自己new一个RequestQueue对象,你就需要自己去做Volley类所做的事情。
(6)CacheDispatcher:这是一个继承Thread的线程,它负责根据用户的Request判断缓存是否有这个请求,如果有这个缓存还要判断它是否过期。如果不存在缓存或者过期那就直接让NetworkDispatcher去网络请求获取。
(7)NetworkDispatcher:这也是一个继承Thread的线程,它负责将用户的Request进行网络请求,当然了,在NetworkDispatcher中是由Network来执行的。
(8)Cache:一个接口,它有两个实现类,分别是DiskBasedCache和NoCache,主要是缓存相关。
(9)ResponseDelivery:它负责传递网络请求的响应,比如错误、结果信息等。
(10)ImageRequest、ImageLoader、NetImageView:这三者是Volley提供对图片的支持,使用Volley来获取网络图片有两种方式,一种是使用ImageRequest请求,和普通的StringRequest请求一样,不过在构造对象时需要传递几个参数,还有一种方式就是使用ImageLoader和NetImageView,这都是Volley提供好了的,后面会详细介绍的。
上面就是Volley框架的一些核心类,下面我们先提出一些问题,然后跟着问题来分析源码:
1、Volley框架的请求、响应机制是怎么设计的?
要回答这个问题,我们从如何创建RequestQueue说起,这里使用最简单的方法来快速获取一个RequestQueue,就是使用Volley.newRequestQueue(mContext)或newRequestQueue(Context context, HttpStack stack)创建RequestQueue对象使用获取的,实际上前者是直接调用了后者来获取一个对象的,来看看Volley类便知,代码不多,我原封不动的贴出来:
public class Volley {
/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//这里创建一个缓存目录,路径就是/Android/data/包名/...
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 {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// 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;
}
/**
* context
* dir sdcard's root dire
* null
* */
public static RequestQueue newRequestQueueInDisk(Context context, String dir, HttpStack stack) {
File cacheDir = new File(dir, 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) {
}
// 如果用户没有自定义HttpStack就使用默认的HurlStack(HttpUrlConnection)和HttpClientStack(HttpClient)请求
//在2.2版本下使用HttpClient,因为2.2版本下不太支持HttpURLConnect,在2.2之上,官方建议使用HttpURLConnect
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//根据协议Stack创建Network
Network network = new BasicNetwork(stack);
//根据缓存目录和Network来创建RequestQueue对象
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//开启队列,
queue.start();
return queue;
}
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir. Context参数用来创建缓存目录
* @return A started {@link RequestQueue} instance.
*这个方法是我门使用时直接调用的,然后Volley又直接调用了重载的newRequest(context,null)
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
可以看到,我们在创建RequestQueue对象时,它已经将队列开启了,此时他的线程都处于等待状态,一旦有新的Request加入进来就去执行。上面 RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);代码最终会调用到其另外一个构造方法:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
上面的参数分别是缓存对象、Network对象、线程池的大小、ExectorDelovery对象,第三个参数默认是4,也就是为网络请求准备了4个子线程,这里重点关注最后一个参数,可以看到在创建ExecutorDelivery对象时获取主线程的Looper来创建了一个Handler对象,这个对象是用于子线程获取结果后和主线程通信时用到的。
下面我们看些RequestQueue的start()方法:
/**
* Starts the dispatchers in this queue.
*/
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();
}
Log.d(TAG, "-->start()");
}
由上面的代码可以看出来,当我们创建RequestQueue对象时就默认调用了start()方法,start()方法会先stop()掉所有已经开启的线程,并重新开启一个缓存读取的线程(CacheDispatcher),以及四个网络请求线程(NetworkDispatcher),那现在我们先去看看CacheDispatch是如何工作的,对了,这里有一个重要的对象没介绍,那就是start()方法中创建CacheDispatcher对象时传递的第一个mCacheQueue和第二个mNetworkQueue参数,看下他们在RequestQueue中的定义:
/** The cache triage queue. */
/** 缓存请求队列*/
private final PriorityBlockingQueue mCacheQueue =
new PriorityBlockingQueue();
/** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue mNetworkQueue =
new PriorityBlockingQueue();
上面两个变量是保证线程同步的关键所在,在start()方法中会将这两个参数传递给CacheDispatcher和NetworkDispatcher,在这两个线程中取出Reques并执行,接下来先看下CacheDispatcher源码,源码中只有两个方法,一个quit()和一个run(),以下是CacheDIspatcher类的全部源码:
/**
* Provides a thread for performing cache triage on a queue of requests.
*
* Requests added to the specified cache queue are resolved from cache.
* Any deliverable response is posted back to the caller via a
* {@link ResponseDelivery}. Cache misses and responses that require
* refresh are enqueued on the specified network queue for processing
* by a {@link NetworkDispatcher}.
*/
@SuppressWarnings("rawtypes")
public class CacheDispatcher extends Thread {
private final String TAG = CacheDispatcher.class.getName();
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;
/**
* Creates a new cache triage dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param cacheQueue Queue of incoming requests for triage
* @param networkQueue Queue to post requests that require network to
* @param cache Cache interface to use for resolution
* @param delivery Delivery interface to use for posting responses
*/
public CacheDispatcher(
BlockingQueue cacheQueue, BlockingQueue networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* Forces this dispatcher to quit immediately. If any requests are still in
* the queue, they are not guaranteed to be processed.
*/
public void quit() {
mQuit = true;
interrupt();
}
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
// 设置线程优先级,Process.THREAD_PRIORITY_BACKGROUND参数表示该线程比普通线程的优先级要稍微低一点,这样不至于对用户界面交互产生较大影响
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.
// 从缓存BlockingQueue中读取Request,如果为空就会阻塞当前线程,当前线程就会一直处于等待状态,知道mCacheQueue中有新的Request
final Request request = mCacheQueue.take();
request.addMarker("cache-queue-take");
Log.d(TAG, "--->run()" + request);
// 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.
// 根据缓存key值来获取缓存,这个缓存key值实际上就是该请求的url
Cache.Entry entry = mCache.get(request.getCacheKey());
// 如果Entry是null说明没有缓存,就添加到mNetworkQueue队列,去从网络获取
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.
// 如果有Entry,判断是否过期,如果过期也从网络获取
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.
// 使用ResponseDelivery对象调用postResponse();
// 这个方法是ExecutorDelivery重写的方法,最终会调用到Response.Listener接口的onResponse()方法,
// 这样就完成了请求消息的响应
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.
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.
// 当调用quit()方法时,线程就结束等待,子线程结束
if (mQuit) {
Log.d(TAG, "缓存线程被吵醒了!退出!");
return;
}
continue;
}
}
}
}
CacheDispatcher类的关键代码都在源码中注释了,这个类就是负责判断该Request是否需要进行网络请求,如果不需要就直接调用 mDelivery.postResponse(request, response);返回给用户。否则就让NetworkDispatcher来执行网络请求。下面来看下NetworkDispatcher的源码:
/**
* Provides a thread for performing network dispatch from a queue of requests.
*
* Requests added to the specified queue are processed from the network via a
* specified {@link Network} interface. Responses are committed to cache, if
* eligible, using a specified {@link Cache} interface. Valid responses and
* errors are posted back to the caller via a {@link ResponseDelivery}.
*/
@SuppressWarnings("rawtypes")
public class NetworkDispatcher extends Thread {
/** The queue of requests to service. */
Handler handler;
private final String TAG = NetworkDispatcher.class.getName();
private final BlockingQueue mQueue;
/** The network interface for processing requests. */
private final Network mNetwork;
/** The cache to write to. */
private final Cache mCache;
/** For posting responses and errors. */
private final ResponseDelivery mDelivery;
/** Used for telling us to die. */
// 使用volatile修饰,线程每次读取时都会读取最后一次修改的值
private volatile boolean mQuit = false;
/**
* Creates a new network dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param queue Queue of incoming requests for triage
* @param network Network interface to use for performing requests
* @param cache Cache interface to use for writing responses to cache
* @param delivery Delivery interface to use for posting responses
*/
public NetworkDispatcher(BlockingQueue queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
/**
* Forces this dispatcher to quit immediately. If any requests are still in
* the queue, they are not guaranteed to be processed.
*/
public void quit() {
mQuit = true;
interrupt();
}
@Override
public void run() {
// 设置线程执行优先级,Process.THREAD_PRIORITY_BACKGROUND参数设置表示这个线程的优先级比普通的线程要稍微低一些
// 所以,这个线程的执行对用户的界面交互的影响比较低
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request request;
while (true) {
try {
// Take a request from the queue.
// 同理,如果mQueue中没有请求就处于阻塞、等待状态。
request = mQueue.take();
Log.d(TAG, "-->run()--" + request);
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
Log.d(TAG, "网络线程被吵醒了,将要退出?!");
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;
}
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
// Perform the network request.
// Network执行,这个方法是BasicNetwork重写的
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.
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.
// 如果需要缓存就加入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back.
request.markDelivered();
// 传递响应
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));
}
}
}
private void parseAndDeliverNetworkError(Request> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
以上就是NetworkDispatcher类的源码了,其解析并传递请求后的Response和CacheDispatcher是一样的,就不罗嗦,主要是它进行网络请求的方法是 NetworkResponse networkResponse = mNetwork.performRequest(request);来执行的,这个方法是BasicNetwork实现的,我们看看这个类的源码,代码略多,我们只看其perfirmRequest()方法吧:
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
// 响应消息头信息
Map responseHeaders = new HashMap();
try {
// Gather headers.
Map headers = new HashMap();
// 获取该请求的缓存并将协议的Head加入到缓存
addCacheHeaders(headers, request.getCacheEntry());
// 执行Http请求
httpResponse = mHttpStack.performRequest(request, headers);
// 获取响应状态
StatusLine statusLine = httpResponse.getStatusLine();
// 响应码
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry().data, responseHeaders, true);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} 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);
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);
}
}
}
}
上面代码呢实际上也是对HttpStack的一种包装了,真正执行请求的还是上面的httpResponse = mHttpStack.performRequest(request, headers);代码,也就是HttpStack的方法,也就是他的两个实现类的方法,我们先来看HttpClientStack的源码,代码略多,就看两个主要的:
@Override
public HttpResponse performRequest(Request> request, Map additionalHeaders)
throws IOException, AuthFailureError {
// 创建一个Http请求
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
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);
}
/**
* Creates the appropriate subclass of HttpUriRequest for passed in request.
*/
@SuppressWarnings("deprecation")
/* protected */ static HttpUriRequest createHttpRequest(Request> request,
Map additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
return new HttpGet(request.getUrl());
}
}
case Method.GET:
return new HttpGet(request.getUrl());
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
default:
throw new IllegalStateException("Unknown request method.");
}
}
下面的方法在上面一个方法中被调用,用于根据不同的请求方式创建不同的请求对象。