从上一篇文章我们可以知道,Volley的整个工作都是建立在一个RequestQueue上的,所以理解RequestQueue对于我们使用Volley是很有必要的,上一篇文章的例子中,我们使用Volley.newRequestQueue()创建了一个RequestQueue实例,那么我们就先来看一看Volley是怎么把RequestQueue创建出来的吧。
public class Volley {
/** 默认的cache数据保存在data/data/包名/volley/cache中 */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* 创建一个默认的RequestQueue实例
* 创建一个RequestQueue需要两个必不可少的东西
* 1.一个network 2.一个cache
* network用于发送网络请求
* cache用于存储缓存
*
* @param context 用来创建cache目录的.
* @return 返回一个RequestQueue实例.
*/
public static RequestQueue newRequestQueue(Context context) {
//部分代码省略,只看核心代码
。。。
HttpStack stack;
if (Build.VERSION.SDK_INT >= 9) {
//判断当前设备的版本信息,如果API大于等于9的话,就使用HttpURLConnection建立网络连接。
stack = new HurlStack();
} else {
// 在API小于9的版本中,HttpURLConnection是存在bug的,所以使用AndroidHttpClient建立连接
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
//BasicNetwork是Volley中默认的Network实现类,他通过传进来的stack来判断是用AndroidHttpClient
//还是HttpURLConnection来创建一个network,这是RequestQueue的第一个必需品
Network network = new BasicNetwork(stack);
//new DiskBasedCache()新建了一个cache实例,这是RequestQueue第二个必需品
//这两个必需品都准备完了,作为RequestQueue的参数,可以新建一个RequestQueue实例
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//注意,在调用Volley。newRequestQueue()的时候,就已经启动了请求队列,请求队列也是一个线程。后面会分析到
queue.start();
return queue;
}
}
通过分析Volley类的源码,我们可以发现Volley在创建网络连接的时候是非常讲究的,我们想想自己原来在建立网络连接的时候,有没有考虑过HttpClient选择的问题。毕竟Volley是Google工程师开发出的框架,他们对自己的产品最熟悉. 我们再来看一下RequestQueue的源码
public class RequestQueue {
//用来生成单调递增的TAG赋给request
private AtomicInteger mSequenceGenerator = new AtomicInteger();
//RequestQueue的第一个必需品
private final Network mNetwork;
//RequestQueue的第二个必需品
private final Cache mCache;
//ResponseDelivery接口,用于将结果交付给主线程
private final ResponseDelivery mDelivery;
//网络管家
private NetworkDispatcher[] mDispatchers;
//缓存管家
private CacheDispatcher mCacheDispatcher;
//这个是存储request的mCurrentRequests,这是一个set,set不允许有重复的数据,
//那么mCurrentRequsts中的request都是唯一的,如果有重复的request怎么办,看下面
private final Set mCurrentRequests = new HashSet();
//mWaitingRequests就是用来存储重复request的,将具有相同url地址的request列成
//一队作为值,然后url作为键,加入到这个map中
private final Map> mWaitingRequests =
new HashMap>();
//缓存队列
private final PriorityBlockingQueue mCacheQueue =
new PriorityBlockingQueue();
//网络队列
private final PriorityBlockingQueue mNetworkQueue =
new PriorityBlockingQueue();
//默认的网络线程池大小为4
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/**
* 在Volley.newRequestQueue()中,调用了queue的start()方法,即此方法,这个方法
* 主要是把CacheDispatcher和NetworkDispatcher启动起来,即调用他们的start()方法
*/
public void start() {
//首先先确保当前没有正在运行的dispatcher,如果有的话,就停止掉
stop();
// 创建一个CacheDispatcher实例
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
//启动CacheDispatcher线程,即启动缓存线程
mCacheDispatcher.start();
//根据线程池数量新建NetworkDispatcher实例(默认为4个)
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
//启动NetworkDispatcher,即启动网络线程
networkDispatcher.start();
}
}
/**
* 停止所有的dispatchers
*/
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
/**
* 在上一篇文章的例子中,我们最后一步就是把request加入到requestQuue中,即调用此方法
*/
public Request add(Request request) {
//给当前的request一个标识,表示他是属于这个requestQueue的,其他的
//requestQueue别管。
request.setRequestQueue(this);
//将当前request添加到mCurrentRequests中,这里面的request都是唯一的
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
//给request赋值一个单调递增的TAG,来标识他们
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 如果这个request是不可缓存的,那么就跳过缓存队列,直接添加到网络队列中
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// 在这里处理重复的request
synchronized (mWaitingRequests) {
//能走到这一步,说明这个request是可缓存的,我们可以通过
//调用getCacheKey()方法,getCacheKey()调用getUrl()方法
//最后其实就是得到的这个request的url地址
String cacheKey = request.getCacheKey();
//检查一下在当前的等待队列中有没有此url
if (mWaitingRequests.containsKey(cacheKey)) {
// 如果有的话,获得具有相同url地址的request,将他们列队
Queue stagedRequests = mWaitingRequests.get(cacheKey);
//如果这个队列为空的话,说明缓存过这个url地址,但是没有request,
//当前request是第一个,那就需要新建一个链表
if (stagedRequests == null) {
//新建一个链表
stagedRequests = new LinkedList();
}
//将此request加入到链表中
stagedRequests.add(request);
//url地址作为键,装有request的链表作为值
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
//没有缓存过这个url,则将url地址作为键,null作为值,下一次再有
//这个url地址的request出现时,可以进行if里面的步骤
mWaitingRequests.put(cacheKey, null);
//将这个request加入到缓存队列中
mCacheQueue.add(request);
}
return request;
}
}
}
总结: 1.在调用Volley.newInstance()之后,就会调用到RequestQueue中的start()方法,RequestQueue中start()方法会调用CacheDispatcher和NetworkDispatcher的start()方法,将缓存管家和网络管家启动起来。 2.在调用add()方法后,一个request就传进来了,给他设置两个标识,一个是它属于这个requestQueue处理,另一个是一个单调递增的TAG。 3.将这个request添加到mCurrentRequests中,这是一个set,如果mCurrentRequests中已经有了这个request,那么它自然是加不进去的,作为重复的request来处理 4.处理重复request。获得这个request的url地址 4.1.如果这个url地址之前被缓存过并且存在存储这个相同request的一个链表,那么就将这个request添加到这个链表中,否则新建一个链表。 4.2.如果这个url地址之前没被缓存过,那么就将url作为键,null作为值插入到这个map中。下一次的时候,就会进行4.1了。 我们可以在项目中的任何地方新建一个RequestQueue,再新建一个Request,add到RequestQueue中后就可以工作了,但是如果我们项目中很多地方都要用到网络请求,那么在很多地方都新建一个RequestQueue肯定是不好的,所以,在使用Volley的时候,我们把RequestQueue设置为全局单例的。
public class AppController extends Application {
public static final String TAG = AppController.class.getSimpleName();
//
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static AppController mInstance;
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized AppController getInstance() {
return mInstance;
}
//获得requestQueue实例,如果没有的话则创建一个新的
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
//在这里调用Volley.newRequestQueue()
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
//获得imageLoader实例,因为我们会加载图片,这个是Volley提供的图片加载器
public ImageLoader getImageLoader() {
getRequestQueue();
if (mImageLoader == null) {
//新建imageLoader需要传入两个参数,第一个为当前的requestQueue
//第二个为设置缓存的大小,LruBitmapCache对缓存做了初始化,后面会介绍
mImageLoader = new ImageLoader(this.mRequestQueue,
new LruBitmapCache());
}
return this.mImageLoader;
}
//将Request添加到requestQueue中的方法,可以给request设置一个tag作为标记
public void addToRequestQueue(Request req, String tag) {
// 如果没有传入tag,那么就将TAG传入作为默认的标记
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
//将TAG作为request的默认标记
public void addToRequestQueue(Request req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
//取消队列中的标记为tag的request
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
public class LruBitmapCache implements ImageLoader.ImageCache {
private android.util.LruCache mCache;
//新建一个LruCache,这是个一级缓存,是缓存到内存上的
public LruBitmapCache() {
//指定大小为15Mb
int maxSize = 15 * 1024 * 1024;
mCache = new android.util.LruCache(maxSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
};
}
//ImageLaoder.ImageCache借口的回调方法,从内存缓存中获取图片
@Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
//将图片缓存到内存中
@Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
}
这样,我们的标准RequestQueue全局单例实例就已经创建好了,我们可以在需要访问网络的地方直接调用AppController.getInstance().addToRequestQueue()就可以开始工作了。 最后在清单文件中将AppController注册进去并且添加网络权限