当调用 RequestQueue的 add()方法添加 Request 的时候,会根据请求的一个参数 shouldCache,来判断要不要去缓存中查询,如果是去缓存中查询,那么就会把请求放到CacheQueue中,如下:
mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request);
这个时候,线程CacheDispatcher其实已经在跑了,到它的run方法中来看一下:
public void run() { if (DEBUG) VolleyLog.v("start new dispatcher"); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // 初始化缓存 mCache.initialize(); while (true) { try { // 从缓存队列中获取一个请求 final Request<?> request = mCacheQueue.take(); request.addMarker("cache-queue-take"); // 如果请求已经被取消,则重新获取请求 if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } // 根据request的cacheKey从缓存中得到对应的记录 Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { request.addMarker("cache-miss"); // 这里说明缓存中没有对应的记录,那么需要去网络中获取,那么就将它放到Network的队列中 mNetworkQueue.put(request); continue; } // 如果缓存中有记录,但是已经过期了或者失效了,也需要去网络获取,放到Network队列中 if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } // 如果上面的情况都不存在,说明缓存中存在这样记录,那么就调用request的parseNetworkResponse方法,获取一个响应Response request.addMarker("cache-hit"); Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed"); if (!entry.refreshNeeded()) { // 缓存记录,不需要更新,那么就直接调用mDelivery,传回给主线程去更新。 mDelivery.postResponse(request, response); } else { // 还存在这样一种情况,缓存记录存在,但是它约定的生存时间已经到了(还未完全过期,叫软过期),可以将其发送到主线程去更新 // 但同时,也要从网络中更新它的数据 request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; // 将其传回主线程的同时,将请求放到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. if (mQuit) { return; } continue; } } }
缓存线程(CacheDispatcher)主要做了几件事情:
1)初始化本地缓存
2)开始一个无限的循环,调用 mCacheQueue的take方法,来获得一个请求,而mCacheQueue是一个BlockingQueue,也就是说,当队列中没有请求的时候,take方法就会一直阻塞在这里,等待队列中的请求,而一旦队列中有新的请求进来了,那么它就会马上执行下去。
/** The queue of requests coming in for triage. */ private final BlockingQueue<Request<?>> mCacheQueue; /** The queue of requests going out to the network. */ private final BlockingQueue<Request<?>> mNetworkQueue;
3)判断请求是否已经取消,如果已经被取消了,则不需要再走下去。
4)根据请求的CacheKey去缓存中寻找相对应的记录,如果找不到对应的记录,或者对应的记录过期了,则将其放到NetworkQueue队列中。
5)缓存中存在相对应的记录,那么调用每个请求具体的实现方法 parseNetworkResponse函数,根据具体的请求去解析得到对应的响应Response对象。
6)获得Response对象之后,还会再进行判断这个请求是不是进行一次网络的更新,这是根据记录的soft-ttl (time-to-live)属性,如下:
/** True if the entry is expired. */ public boolean isExpired() { return this.ttl < System.currentTimeMillis(); } /** True if a refresh is needed from the original data source. */ public boolean refreshNeeded() { return this.softTtl < System.currentTimeMillis(); }
从这里也可以看到,expired的判断跟refreshNeed的判断是两个字段,一个是ttl,一个是softTtl。
如果需要进行更新,那么就会在发送响应结果回主线程更新的同时,再将请求放到NetworkQueue中,从网络中更新请求对应的数据。如果不需要,则直接将结果调用mDelivery传回主线程进行UI的更新。
CacheDispatcher做的事情并不多,因为Volley主要的功能其实还是跟网络打交道,所以主要的实现,其实还是NetworkDispatcher。
结束!