涉及到的知识点:
- Volley网络请求的流程
- 网络分发器逻辑
- 缓存分发器逻辑
准备
在正式开始分析Volley源码逻辑之前,有两件事需要搞明白:
- Volley是怎么使用的?
- 请求的流程是怎样的?
我们先从基本的使用开始吧。
Volley的使用方式
我们以通过点击Button加载图片为例,来说明一下Volley的使用。其实很简单,就是在Activity的布局里面放置一个Button,监听它的点击事件,点击逻辑如下:
//实例化消息队列
RequestQueue requestQueue = Volley.newRequestQueue(this);
//实例化图片请求
ImageRequest imageRequest = new ImageRequest("http://wanandroid.com/resources/image/pc/logo.png",
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
mIvShowDownloadImage.setImageBitmap(response);
}
}, 0, 0, Bitmap.Config.RGB_565,
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mIvShowDownloadImage.setImageResource(R.mipmap.ic_launcher);
}
});
//设置不缓存(后面会讲到缓存逻辑)
imageRequest.setShouldCache(false);
//将请求添加到缓存队列中,开始执行
requestQueue.add(imageRequest);
上面逻辑的结果如下:
Volley网络请求流程
学习Volley的网络请求流程,我们上一张官方的原理图吧,然后简单说明一下大概的流程,在后面我们会详细分析。
根据上图,结合上一小节我们来大致的说明一下网络请求的流程:
- 构建网络请求实体类对象(Request抽象类实现类对象),将其添加到请求队列中(默认会添加到缓存队列中)
- 在默认情况下,会去判断缓存是否命中(当前请求的缓存key和已经缓存的缓存结果的key是否相同),命中(存在相同缓存key)则直接从缓存中取请求结果返回给请求。如果没有命中将请求添加到网络请求队列中
- 网络请求队列执行真正的网络请求,然后解析响应,写入缓存,回调结果监听等
说实话感觉并没有什么内容,但是之所以要分析源码,就是为了学习思想,学习架构,好吧装了一波逼。。。下面开始正文。
我们从RequestQueue requestQueue = Volley.newRequestQueue(this);
开始追踪源码,可以发现它会调用重载的newRequestQueue函数,我们去看一下重载的函数做了什么:
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info =
context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}
return newRequestQueue(context, network);
}
看着是不少,实际上就是实例化了一个network参数,然后再次调用另一个重载的newRequestQueue函数。这里的话有两点:
第一:network参数,是一个BasicNetwork的实例,它是接口Network的实现类,是执行网络请求的执行类;
第二:重载的newRequestQueue函数,我们点击进去看一下,它里面做了什么:
private static RequestQueue newRequestQueue(Context context, Network network) {
//初始化缓存文件
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
//实例化请求队列
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//队列开始分发请求
queue.start();
return queue;
}
可以发现其实一共就做了三个操作,操作一:创建缓存文件;操作二:调用RequestQueue的构造函数,构建了一个请求队列;操作三:开始分发请求。构建RequestQueue的话就是初始化了一些参数,重点在于start函数,我们去看一下:
/**
* 分发当前队列的请求
*/
public void start() {
//确保当前(其实是之前的,因为我们要使用新的,所以要把之前的全部清空)正在运行的所有分发器全部退出(不论是网络请求的,还是缓存的)
stop();
// 创建新的缓存请求分发器,并开始(分发器其实就是线程)
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
//调用线程的start方法
mCacheDispatcher.start();
// 创建新的网络请求分发器,并开始执行(网络请求分发器默认是4个实例)
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
可以发现,这里面主要就是初始化缓存分发器和网络分发器,并调用它们的start方法,那么这些分发器到底是什么鬼呢?我们一个个来看,先从网络分发器开始吧。
网络分发器
点击进入NetworkDispatcher看一下源码,发现原来它的本质就是一条线程啊,重写了run方法,并且在run写了一个死循环,不断的执行processRequest函数,具体源码如下:
public class NetworkDispatcher extends Thread {
/** The queue of requests to service. */
private final BlockingQueue<Request<?>> 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. */
private volatile boolean mQuit = false;
public NetworkDispatcher(
BlockingQueue<Request<?>> 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();
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void addTrafficStatsTag(Request<?> request) {
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
}
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//跟轮训器一样,不断调用执行请求的函数
while (true) {
try {
//执行请求
processRequest();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
}
}
}
private void proc