一、概述
从上一篇博客中我们已经了解了 RequestQueue ,如果对 RequestQueue 不是很了解那么不妨先看看上篇博客 ( 一 ) Volley 源码深入了解之 RequestQueue,本篇博客将会接着上篇的分析继续从源码的角度深入了解 CacheDispatcher 和 NetworkDispatcher 是如何工作的
二、 深入了解 CacheDispatcher
在分析之前,照列先看官方给的流程图
经过上篇的分析创建 request 添加到 RequestQueue 过程我们已经很清楚了,而两个 Dispatcher 内部的工作流程我们还没有很深入的了解,这一节我们先看看 CacheDispatcher
上一篇我们说过,当调用 RequestQueue 的 start 方法时会创建 CacheDispatcher 和 NetworkDispatcher 对象 ,现在我们来回顾一下
public class RequestQueue {
/** 启动所有 dispatcher */
public void start() {
stop(); // 启动之前先停止所有 dispathcer
// 在这里创建了缓存调度器并启动
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 在这里启动网络调度器线程池(数量默认为4)并全部启动
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
/** 停止调度 */
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();//调用 Thread 的 interrupt 方法终止线程
}
//终止线程池
for (final NetworkDispatcher mDispatcher : mDispatchers) {
if (mDispatcher != null) {
mDispatcher.quit();//同上
}
}
}
//省略若干代码.....
}
可以看到先创建了 CacheDispatcher 对象并启动了,我们直接进入 CacheDispatcher 中看看
public class CacheDispatcher extends Thread {
//省略无关代码
/** 缓存队列 */
private final BlockingQueue<Request<?>> mCacheQueue;
/** 网络请求队列 */
private final BlockingQueue<Request<?>> mNetworkQueue;
/** 要读取的缓存 */
private final Cache mCache;
/** 用于发送获取 response 到主线程 */
private final ResponseDelivery mDelivery;
/** 线程终止标记. */
private volatile boolean mQuit = false;
/**管理等待中的请求和避免相同的缓存 key 进行重复的请求 */
private final WaitingRequestManager mWaitingRequestManager;
/**
* CacherDispatcher 的唯一构造方法
*/
public CacheDispatcher(
BlockingQueue<Request<?>> cacheQueue,
BlockingQueue<Request<?>> networkQueue,
Cache cache,
ResponseDelivery delivery) {
//上面的注释已经说明,不在赘述
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
mWaitingRequestManager = new WaitingRequestManager(this);
}
//省略若干代码.......
}
可以看到 CacherDispatcher 继承自 Thread ,那就好办了我们直接找 run 方法就行了
public class CacheDispatcher extends Thread {
//省略无关代码
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
//设置线程的优先级,默认为后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化缓存(线程安全的方法)
mCache.initialize();
//死循环,保证一直在后台轮询,取出缓存队列中的 request 处理
while (true) {
try {
//处理 request
processRequest();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
}
}
}
//省略若干代码.......
}
run 方法中首先对缓存进行了初始化,然后调用了处理 request 的方法我们跟进看看
private void processRequest() throws InterruptedException {
// 从缓存队列中取出 request
final Request<?> request = mCacheQueue.take();
processRequest(request);
}
/**
* 这个方法对缓存的各种状态进行判断并处理
*/
void processRequest(final Request<?> request) throws InterruptedException {
request.addMarker("cache-queue-take");
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED);
try {
// 如果这个请求已被 cancel 则结束请求并停止该方法的执行
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}
// 通过 request 的 cache key 恢复当前 reqeust 的 cache 信息
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {//缓存为 null
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
//判断当前的 request 是否被添加到了等待列表中
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
//没有缓存,那就加入的网络请求队列中
mNetworkQueue.put(request);
}
return;
}
// 恢复的 request 缓存是否过期
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);//过期且不再等待列表中则加入到网络请求队列
}
return;
}
// 如果上面的判断都没有问题,则解析缓存信息并传递这个请求到 main thread
request.addMarker("cache-hit");
Response<?> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
//这里通过 softTll 来判断是否过期
if (!entry.refreshNeeded()) {
//没有过期则直接返回 response
mDelivery.postResponse(request, response);
} else {
// 这里的处理就比较有意思了,如果过期了依然会先返回缓存的response
// 然后在把这个 request 加入网络请求队列,这就是上一节中 softTtl 的作用
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
//可以看到这里面的逻辑正是前面说的
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(
request,
response,//缓存 response
new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);//加入网络请求队列
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
});
} else {
// request has been added to list of waiting requests
// to receive the network response from the first request once it returns.
mDelivery.postResponse(request, response);
}
}
} finally {//发送请求完成的事件
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
}
}
代码很长,但并不复杂,可以从中看到 processRequest 方法对出队的 request 进行了非常严谨的处理和判断,包括取消状态,缓存是否为null, 是否过期,是否在等待的请求列表中等等,但是总体来说干的事情并不复杂,说的直白一点就是出队,判断请求是否可用,可用则直接传递到主线程,不可用则加入到网络请求队列,只是中间做了完善的处理。不过有两个地方需要深入探究一下,看看下面这行代码
mWaitingRequestManager.maybeAddToWaitingRequests(request)
这行代码多次出现,前面也有对它的简单解释,但它到底干了什么呢? 它是如何处理重复请求的呢? 想搞清楚的话,那么就得深入研究一下 WaitingRequestManager 这个类了,先让我们看下这个类的基本结构
private static class WaitingRequestManager implements Request.NetworkRequestCompleteListener {
//这个map的作用相当于是对于重复请求的一个暂存区域
private final Map<String, List<Request<?>>> mWaitingRequests = new HashMap<>();
private final CacheDispatcher mCacheDispatcher;
//与 CacheDispathcer 关联
WaitingRequestManager(CacheDispatcher cacheDispatcher) {
mCacheDispatcher = cacheDispatcher;
}
@Override //处理接收到的 response
public void onResponseReceived(Request<?> request, Response<?> response) {}
@Override //处理不可用的无效的 response
public synchronized void onNoUsableResponseReceived(Request<?> request) {
//处理等待中的请求,一般就是重复的请求
private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {}
}
注释中已对各个字段和方法做了一个简短说明,但这里还是要说明一下,WaitingRequestManager 这个类是 CacheDispathcer 的私有内部类,实现了 Request 的内部接口 NetworkRequestCompleteListener 并重写了两个方法,如上面的代码所示,可以看到只有maybeAddToWaitingRequests 是该内部类的自有方法,我们看看它做了什么
private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {
String cacheKey = request.getCacheKey();//获取传入的 request 的 cache key
//判断这个请求是否已经在集合中
if (mWaitingRequests.containsKey(cacheKey)) {
// 如果有则加入暂存列表其实就是重复的请求列表
List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new ArrayList<>();
}
request.addMarker("waiting-for-response");
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
return true; //包含重复的 request 返回 true
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
//如果不包含则与传入的 request 对象关联方便 request 调用
request.setNetworkRequestCompleteListener(this);
if (VolleyLog.DEBUG) {
VolleyLog.d("new request, sending to network %s", cacheKey);
}
return false; //不包含返回 false
}
}
可以看到 maybeAddToWaittingRequests 方法内部的逻辑并不复杂,当等待集合中有这个缓存时则 add 到暂存列表中,并返回 true ,否则将等待列表置为 null 且与传入的 Request 进行关联,并返回 false, 这有什么用呢? 其实结合 processRequest 中的代码看,理解这个方法作用还是很简单的,当缓存的 request 已经过期或为null 但又重复请求时,则会被添加到等待的暂存列表中等待被调用,并且把这个 reqeust 加入到网络请求队列中,且结束本次的 CacheDispatcher 轮询,相当于一次 cache miss 也就是没有拿到缓存,没有拿到缓存的话那么就由 NetworkDispatcher 来处理这个 request 了。至于 onResponseReceived 和 onNoUsableResponseReceived 方法这里先卖个关子放到后面讲
分析完 maybeAddToWaittingRequests 后我们来思考一下,缓存中的数据是如何被传递到主线程中从而被我们拿到的呢? 换句话说 Volley 是如何把获得的 response 传递给调用者的呢?
让我们回忆一下我们是从哪里获取的数据
String url = "https://www.baidu.com";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());//创建一个请求队列
StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
}
});
request.setTag("volley");
mRequestQueue.add(request);
mRequestQueue.start();
可以看到是在 Respons 的内部接口 Listener 中的 onResponse 中拿到的数据,在前面的分析中似乎没有发现这个接口的调用痕迹,那么 response data 是怎么过来的呢?答案就是通过 ExecutorDelivery 这个对象, 其实 ExecutorDelivery 这个对象在上一篇出现过,如果你看的仔细那么肯定有印象,就是在 4.2.3 深入了解 RequestQueue 小节中的开头部分,在 RequestQueue 的构造函数中被实例化并被传入到 CacheDispathcer 和 NetworkDispacher 中。我们来看看这个类的具体内容
// respinse 传递的接口
public interface ResponseDelivery {
/** 将从网络或缓存获取的 response 解析并传递 */
void postResponse(Request<?> request, Response<?> response);
/**
* 功能同上,不同的是提供了一个 Runnable 接口在传递 response 后执行任务
*/
void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/** 将错误信息 post 给指定的请求. */
void postError(Request<?> request, VolleyError error);
}
// ResponseDelivery 的具体实现类,用于传递 response 到主线程
public class ExecutorDelivery implements ResponseDelivery {
//用于在主线程传递 response 的执行接口,该接口下的 excute 方法接收一个 Runnable 接口
private final Executor mResponsePoster;
/**
* 接收 Handler 的构造函数
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {//提供了 Executor 的默认实现
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* 接收一个 Executor ,可以用来自定义你的传递方式
* @param executor For running delivery tasks
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
private static class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;//传入的 request
private final Response mResponse;//要传递的 response
private final Runnable mRunnable;//传递后执行的 Runnable
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// 如果这个请求被取消则停止请求,并终止传递
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 传递错误或者成功的 response 取决于 isSuccess
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// 这是一个中间相应,当 softTll 过期时会调用
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// 这里表示传递完 response 后可以执行一个 Runnable
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
可以看到代码并不复杂,ExecutorDelivery 是 ResponseDelivery 接口的具体实现类实现了三个 post 方法,主要用来向主线程传递成功或失败的 response ,而且 ExecutorDelivery 共有两个构造函数,第一个接收一个 Handler 对象来传递 response 默认提供了一个 Excutor 接口的实现。第二个则接收一个 Excutor 接口,用来自定义你的传递方式,其中 ExecutorDelivery 还有一个私有的实现了 Runnable 接口的静态内部类 ResponseDeliveryRunnable 专门用来处理具体传递任务,内部的 run 方法逻辑很简单就不多说了,我们重点看下 mRequest.deliverResponse 方法(这是 Request 对象的方法),从中我们可以看到这行才是真正传递 response 的代码,我们进入 Request 对象看看
public abstract class Request<T> implements Comparable<Request<T>> {
protected abstract void deliverResponse(T response);
public void deliverError(VolleyError error) {
Response.ErrorListener listener;
synchronized (mLock) {
listener = mErrorListener;
}
if (listener != null) {
listener.onErrorResponse(error);
}
}
//省略若干代码
}
可以看到 Request 是一个实现了 Comparable 接口的抽象类,这个抽象类主要是对一个 request 的一些共有特性进行了抽离,例如设置 request 的 GET/POST 、Header、request 的参数等等,这些很简单就不多说了,我们就重点看下上面的两个方法,deliverResponse/Error 方法是用来将 response 回调给主线程的,其中 Request 已经提供了默认的 deliveryError 方法,而 deliveryRespose 则需要子类去实现,比如 StringRequest ,JsonRequest 等等。
这里提一下 Response 这个对象, 顾名思义这个对象是对从网络或缓存中获取的 response 的一个封装类,内部定义了两个接口如下列代码
public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
void onResponse(T response);
}
/** Callback interface for delivering error responses. */
public interface ErrorListener {
/**
* Callback method that an error has been occurred with the provided error code and optional
* user-readable message
*/
void onErrorResponse(VolleyError error);
}
//省略若干代码
}
是不是很眼熟呢,没错这就是我们最终拿到 response / error 信息的两个接口,当调用 deliveryError 的时候其实最终调用的就是 Response 的内部接口 ErrorListener 的 onErrorResponse 方法。 deliveryResponse 也是同理,用的都是一个逻辑,这个可以看下 StringRequest 中的实现,一样的方式,唯一和 error 不同的地方在于调用的是 Response 的内部接口 Listener 的 onResponse 方法(读者可以自己看下 Volley 提供的几个默认 Request 查看 ),这里就不多赘述了。
现在再看下面的代码是不是觉得对内部的工作机制有了一个更为清晰的认知呢
String url = "https://www.baidu.com";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());//创建一个请求队列
StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
}
});
request.setTag("volley");
mRequestQueue.add(request);
mRequestQueue.start();
三、深入了解 NetworkDispatcher
NetworkDispatcher 是一个网络请求调度器,当 CacheDispatcher 没有发现缓存信息时,NetworkDispatcher 就会从请求队列中获取 Request 并发起这个网络请求
/** 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();
}
}
可以看到在 RequestQueue 中的 start 方法中创建了 NetworkDispatcher 线程池(默认是长度为 4),构造函数中传入的参数和 CacheDispatcher 差不多唯一的区别就是少了一个 CacheQueue ,由于和 CacheDispatcher 的结构差不多,这里就不详细介绍 NetworkDispatcher 的类结构了,我们直接进入 run 方法
@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) {
Thread.currentThread().interrupt();
return;
}
VolleyLog.e(
"Ignoring spurious interrupt of NetworkDispatcher thread; "
+ "use quit() to terminate it");
}
}
}
和 CacheDispathcer 的 run 方法差不多,内部也是声明了一个 processRequest 方法来处理请求,跟进看看
private void processRequest() throws InterruptedException {
// 从请求队列中获取请求
Request<?> request = mQueue.take();
processRequest(request);
}
void processRequest(Request<?> request) {
long startTimeMs = SystemClock.elapsedRealtime();
request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
try {
request.addMarker("network-queue-take");
// 如果当前 request 已经取消则停止这个 request
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}
addTrafficStatsTag(request);
// 执行这个网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
//如果服务端返回的数据和上次相同且已经传递过了,则停止重复的请求
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}
// 解析从网络中获取的 response
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果这个 request 需要写入缓存,则更新缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 标记这个请求已经传递过
request.markDelivered();
//直接将获取的 response 传递给主线程
mDelivery.postResponse(request, response);
//一个提示接收 response 的监听
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
request.notifyListenerResponseNotUsable();
} 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);
request.notifyListenerResponseNotUsable();
} finally {//发送网络请求调用结束的事件
request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED);
}
}
从上面的代码中可以看到 NetworkDispatcher 和 CacheDispatcher 对于 request 处理其实大同小异,总体的逻辑处理其实是相同的,很好理解,只是侧重点不同。这里面唯一让我感兴趣的是两个 notify 方法,这两个 notify 方法最终会调用 Request.NetworkRequestCompleteListener 的实现类 WaitingRequestManager 中的两个对应的方法, 如下所示
private static class WaitingRequestManager implements Request.NetworkRequestCompleteListener {
@Override //处理接收到的 response
public void onResponseReceived(Request<?> request, Response<?> response) {}
@Override //处理不可用的无效的 response
public synchronized void onNoUsableResponseReceived(Request<?> request) {
}
前面我们已经分析过这个类,并卖了个关子,(如果不清楚可以回顾一下前面关于 maybeAddToWaitingRequests 的分析)下面我们就来逐一看看这两个方法到底干了什么
- onNoUsableResponseReceived
// 处理无效的 response 方法
@Override
public synchronized void onNoUsableResponseReceived(Request<?> request) {
String cacheKey = request.getCacheKey();//获取该请求的缓存 key
//从缓存的等待集合中移除并获取这个等待的请求列表
List<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null && !waitingRequests.isEmpty()) {
if (VolleyLog.DEBUG) {
VolleyLog.v(
"%d waiting requests for cacheKey=%s; resend to network",
waitingRequests.size(), cacheKey);
}
//从等待的请求列表中移除该 request 并获取下一个
Request<?> nextInLine = waitingRequests.remove(0);
//更新map集合
mWaitingRequests.put(cacheKey, waitingRequests);
//与下一个 request 进行关联
nextInLine.setNetworkRequestCompleteListener(this);
try {//将下一个请求加入到网络请求队列
mCacheDispatcher.mNetworkQueue.put(nextInLine);
} catch (InterruptedException iex) {
VolleyLog.e("Couldn't add request to queue. %s", iex.toString());
// Restore the interrupted status of the calling thread (i.e. NetworkDispatcher)
Thread.currentThread().interrupt();
// Quit the current CacheDispatcher thread.
mCacheDispatcher.quit();
}
}
}
当 request 调用这个方法时,说明这个 request 已经被取消或是一个重复的但 response 没有发生改变的状态。而这个方法就是用来处理这种情况,当这种情况的 Request 传入该方法后,会
先从 mWaitingRequests 这个 map 集合中取出该缓存 key 的等待 list 并移除,然后就是从中取出下一个请求更新 map 集合,并将其加入到网络请求队列中,其总体的思想就是,把这个无用的请求从等待列表中移除,取出另一个等待中的 request 加入 networkQueue 等待被调用。
- onResponseReceived
//处理成功拿到 response 的 request
@Override
public void onResponseReceived(Request<?> request, Response<?> response) {
//没有被缓存过或已经过期,则当做无效的 request 处理
if (response.cacheEntry == null || response.cacheEntry.isExpired()) {
onNoUsableResponseReceived(request);
return;
}
//获取缓存 key
String cacheKey = request.getCacheKey();
List<Request<?>> waitingRequests;
synchronized (this) {//从等待的 map 或者也可以理解成重复的请求集合中移除
waitingRequests = mWaitingRequests.remove(cacheKey);
}
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v(
"Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// 处理所有等待中的请求
for (Request<?> waiting : waitingRequests) {//
mCacheDispatcher.mDelivery.postResponse(waiting, response);
}
}
}
这个方法会在 request 成功拿到 response 后被调用,其实这个方法和 onNoUsableResponseReceived 的方法很相似,也是先把这个 request 从等待的 map 集合中删除并去除对应的 等待列表,唯一不同的是上一个方法时把取出的 request 加入到网络请求队列中,这里是直接全部传递到主线程中去了。刚开始看到这里的时候我是有些疑惑的,因为有好几个地方调用了 mDelivery.postResponse 方法那我们拿的到底是哪一个 response 呢?
其实大可放心,还记得这个方法吗?
// 标记这个请求已经传递过
request.markDelivered();
当调用这个方法时,该 request 的 response 就被标记为已传递过了,那么此时 hasHadResponseDelivered 方法返回的就是 true ,这时请求会被 Volley 停止,同时该请求会被 onNoUsableResponseReceived 方法加入到等待列表中和网络请求对列中等待下次轮询被调用。
至此,Volley 官方给出的流程图到这里算是分析完了。
四、结语
经过两篇的源码深入分析,到这就算是结束了。此时再回过头看下官方给的流程图发现理解的更透彻了,当然 Volley 还有很多的细节值得探讨,但我想看的是整体的设计思想,所以更深的细节没有过多深究,也没必要过于深究将自己陷入牛角尖影响学习的心态。而阅读源码就是要从宏观的层面理解作者的设计思想,从而结合到实际运用到工作中,只有达到这样的程度才是真的学以致用!
IT 人员谁不想写出让人称赞的代码呢? 而通往这条路的捷径就是阅读更多更优秀的源码,也只有这样才能更快的掌握编程的 “里子” !就像 Linux 之父 林纳斯·托瓦兹 说的那样,Talk is cheap. Show me the code.
来源:CSDN
作者:澜秋
链接:https://blog.csdn.net/Mr_lanqiu/article/details/103487942