二、Volley源码解析

2.1 Volley框架结构

根据Volley的框架结构,在上一章可以看出所有的Request请求都是按照这个流程处理,那么我将从请求流程进行源代码分析.
其框架主要分为3部分:
(1)Main Thread中创建Request以及解析显示请求返回结果;
(2)Cache Thread在Cache中处理请求,若请求的内容在缓存中已存在,则从缓存中取出并返回;
(3)NetWork Thread,当请求在缓存中找不到时,则需要从访问网络获取数据.

主线程和Cache线程都只有一个,而NetWork Thread线程可以有很多个(默认是4个),这样能够解决并行问题.

2.2 Volley之RequestQueue

RequestQueue请求队列作为Volley框架使用过程第一个需要创建目标,其内部通过调用Volley类的静态函数进行创建.

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR)
...//省略代码:主要内容
//1、创建UserAgent用来封装应用的包名跟版本号,提供给服务器
//2、根据当前系统的版本号来选择HttpStack,若版本大于等于9(Android 2.2以上),使用HttpURLConnection,所以使用一个HurlStack;若小于9,则使用HttpClient.
//3、创建一个NetWork,调用其构造函数并传入参数stack,去跟网络进行通信.
//4、创建一个DiskBasedCache对象,和Network一起,传给RequestQueue作为参数,创建RequestQueue对象.
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);//创建RequestQueue
queue.start();//调用RequestQueue的start方法
}

请求队列作为Volley中所有请求的存储器,其内部使用Set集合来存储创建的Request,所有在队列中或者正在被处理的请求都会在这个集合中.
private final Set

  public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;//缓存
mNetwork = network;//网络
mDispatchers = new NetworkDispatcher[threadPoolSize];//网络访问线程池
mDelivery = delivery;//派送Response的结果给主线程
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);//默认DEFAULT_NETWORK_THREAD_POOL_SIZE=4
}

其主要工作有:初始化Disk Cache缓存路径、执行网络请求接口NetWork、网络请求调度器NetworkDispatcher和请求结果派送者ResponseDelivery.
在上述创建过程中,无论是CacheDispatcher还是NetworkDispatcher都是继承至Thread.

public class NetworkDispatcher extends Thread {
...//省略代码
}
public class CacheDispatcher extends Thread {
...//省略代码
}

ResponseDelivery其实是一个接口,其具体实现类似ExecutorDelivery,其构造函数的参数是一个Handler,而Handler的构造函数参数则是Lopper.getMainLooper(),所以这里其实是应用的主线程Looper,也就是说handler其实就是主线的Handler,其作用就是将请求的结果(正确的或者错误的)传输给主线程.

/**
* Delivers responses and errors.
*/

public class ExecutorDelivery implements ResponseDelivery {
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster;

/**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/

public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
...//省略代码
}

在创建完成RequestQueue对象之后会调用start方法启动所有的dispatcher(CacheDispatcher和NetworkDispatcher):

  /**
* start方法的作用就是启动所有的dispatcher(CacheDispatcher和NetworkDispatcher)
*/

public void start() {
stop(); //保证当前所有的正在运行的Dispatcher都停止
//创建缓存的调度器(也是一个线程),并启动线程
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();

//根据线程池的大小,创建相对应的NetworkDispatcher(线程),并启动所有的线程
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}

对应start方法当然就有stop方法,用于停止cache and network dispatchers:

   public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}

在start过程中,出现mCacheQueue, mNetworkQueue,这两个的定义如下:

//带有优先级cache请求队列
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();

/** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();

可以看到它们其实都是Java并发(Concurrent)包中提供的利用优先级来执行的阻塞队列PriorityBlockingQueue.显然它们就应该是用来放置从外面传进来的请求,比如JsonRequest,ImageRequest和 StringRequest.
在上述分析了怎么启动怎么停止,那么当创建好Request对象时,怎么样才能添加到请求队列中呢?RequestQueue提供了一个add方法,用于将创建好的Request添加到请求队列中.并判断请求的是否存储于缓存中来进行分类.

public <T> Request<T> add(Request<T> request) {
// 将请求所在队列设置为当前队列,并将请求添加到mCurrentRequests中,表明是正在处理中,并且这里用synchronized来同步
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
//在这里会设置序列号,保证每个请求都是按顺序被处理的。
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");

// 如果这个请求是设置不缓存的,那么就会将其添加到mNetworkQueue中,直接去网络中获取数据
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}

//到这里,表明这个请求可以去先去缓存中获取数据。
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();//key是一个字符串,由Method + ":" + Url组成,默认是Url作为cacheKey
if (mWaitingRequests.containsKey(cacheKey)) {.
//如果这个请求已经有一个相同的请求(相同的CacheKey)在mWatingRequest中,
//那么就要将相同CacheKey的请求用一个LinkedList给装起来,先不需要处理,等那个正在处理的请求结束后,再看看应该怎么处理
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
//如果mWaitingRequest中没有,那么就将其添加到集合中,将添加到mCacheQueue队列中,表明现在这个cacheKey的请求已经在处理了.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}

当mCacheQueue或者mNetworkQueue利用add方法添加请求之后,在运行的线程就会接收到请求,从而去处理相对应的请求,最后将处理的结果由mDelivery来发送到主线程进行更新.
当请求在缓存线程中或者是在网络线程中处理完成后,每个Request都会去调用对应的finish方法,

 void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
onFinish();
}
...//省略代码
}

下一步会调用RequestQueue的finish()方法:

<T> void finish(Request<T> request) {
//1、从当前队列中移除对应的请求
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);//请求结束监听
}
}
//2->
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
}
}
}
}

第2步中,就是判断这个请求有没有缓存,1)如果有,那么我们这个时候,将前面mWaitingQueue中相同CacheKey的一大批请求再扔到mCacheQueue中,为什么现在才扔呢?因为前面我们不知道相同CacheKey的那个请求到底在缓存中有没有;2)如果没有,它需要去网络中获取,那就等到它从网络中获取之后,放到缓存中后,它结束后且已经缓存了,这个时候,我们就可以保证后面那堆相同CacheKey的请求可以在缓存中去取到数据了,而不需要再去网络中获取了.
最后在RequestQueue中还提供了2个方法用于用户自己随时可控地取消请求:

//根据顾虑规则进行请求的取消
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}

/**
* 根据请求的Tag进行取消
*/

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;
}
});
}

RequestFilter是RequestQueue的内部接口,没有具体实现,上面第二个cancleAll方法的过滤规则就是看Tag是否相等.
到此RequestQueue的源代码核心内容就已经分析完成了.

2.3 Volley之Request

Volley提供的Request有:StringRequest、JsonArrayRequest、JsonObjectRequest、ImageRequest和JsonRequest,其中JsonArrayRequest、JsonObjectRequest继承至JsonRequest,StringRequest、ImageRequest、JsonRequest继承至Request.Volley还可以自定义Request.
Request是一个抽象类.提供了很多的方法,子类需要实现的两个抽象方法为:

    /**
子类必须实现这个方法,用于将解析response后的数据传递给request的监听者,解析失败则不会传递.
*/

abstract protected void deliverResponse(T response);

/**
子类必须实现该方法,用于解析网络请求返回结果response并返回一个恰当的response-type,这个方法将被一个工作线程调用.
* @return The parsed response, or null in the case of an error
*/

abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

具体使用已在前一章讲过,无论是Volley中提供的request还是我们自定义的request,都会通过各自的解析方法解析自定义的数据.

2.4 HttpStack

前面准备了那么多,为了就是进行网络请求,那具体的网络请求是怎么样子的呢?
具体的网络请求实现是在HurlStack/HttpClientStack中实现的,还记得在Volley中创建请求队列时对当前系统版本进行判断,为了就是获取不同网络请求框架:

if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {//若版本大于9(Android2.3)则使用HurlStack,其中使用的是HttpURLConnection进行网络请求
stack = new HurlStack();
} else {
////若版本小于9(Android2.3)则使用HttpClientStack其中使用的是HttpClient进行网络请求
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//再通过NetWork类进行调用具体的网络请求实现类
Network network = new BasicNetwork(stack);

其中NetWork是一个接口,它只包含一个方法: public NetworkResponse performRequest(Request

public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {//...省略代码}
//具体网络请求实现调用
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
//...省略代码
// 添加头部信息
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);//调用真正的网络请求类
//各种网络请求返回值判断

//结果返回没有出错的网络请求返回数据
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
}

NetworkResponse相当于一个Bean,其作用是将网络请求返回的数据、各种状态码、请求消耗时间等进行封装.

2.5 NetworkDispatcher

网络请求了解了,那管理网络请求的线程呢?其实就是NetworkDispatcher,当用户提交request之后,若CacheDispatcher中没有对应的请求缓存,就必须要进行网络请求,那这时的就需要NetworkDispatcher线程来进行管理了.

@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
try {
// 从队列中获取一个请求,若没有则一直阻塞
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 (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}

addTrafficStatsTag(request);

// 调用mNetwork去跟网络打交道
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");

// 如果服务器返回一个未修改(304)的响应,并且这个请求已经发送过响应对象,不需要再继续,因为没改过
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}

// 解析响应的数据,返回Response对象
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");

// 根据request的shouldCache字段来判断是不是需要缓存,如果需要,则将其放到mCache中。
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}

// 调用 mDelivery将Response对象传回主线程进行UI的更新。
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,将错误信息传回到主线程,进行提示
mDelivery.postError(request, volleyError);
}
}
}

NetworkDispatcher线程主要完成了:
1) 调用 mQueue的take()方法从队列中获取请求,如果没有请求,则一直阻塞在那里等待,直到队列中有新的请求到来。
2) 判断请求有没有被取消,如果被取消,则重新获取请求。
3) 调用Network对象将请求发送到网络中,并返回一个 NetworkResponse对象。
4) 调用请求的pareseNetworkResonse方法,将NetworkResponse对象解析成相对应的Response对象。
5) 判断请求是否需要缓存,如果需要缓存,则将其Response中cacheEntry对象放到缓存mCache中。
6) 调用 mDelivery将Response对象传到主线程中进行UI更新。
Volley框架中主要的类和流程就如上面所述,其中还有很多小细节没有描述,但是根据Volley使用的流程完全能够看得懂其源代码.

更多相关文章

  1. Android监控WIFI和GSM状态并绘制网络强度
  2. Java 网络 IO 模型
  3. Java使用socket网络编程实现多人聊天室
  4. 20155320 《Java程序设计》实验五网络编程与安全实验报告
  5. Android 网络框架学习之Retrofit
  6. 使用drawables的IntelliJ Android缓存构建忽略了更改
  7. Android中获取网络天气数据
  8. Android Studio Gradle 缓存文件夹设置
  9. 关于Android4.0之上的ListView显示从网络上获取图片和文字

随机推荐

  1. 在each()函数内部调用多个ajax ..然后在完
  2. 在Ajax请求后添加了自闭标记
  3. Jquery:JS弹出窗口DIV层效果
  4. 如何使用ajax GET或POST方法将数据传递到
  5. 如何从SQL SELECT查询中的c#变量创建jQuer
  6. 在Chrome中使用AJAX发送选项而不是GET/PO
  7. jQuery实现点击全选和取消全选
  8. 适用于prettyPhoto的FLV播放器(基于jQuery
  9. 使用Ajax+JQuery构造分页查询列表
  10. 通过jQuery设置全局Ajax加载时呈现Loadin