1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.volley; 18 19 import java.util.concurrent.BlockingQueue; 20 21 /** 22 * Provides a thread for performing cache triage on a queue of requests. 23 * 24 * Requests added to the specified cache queue are resolved from cache. 25 * Any deliverable response is posted back to the caller via a 26 * {@link ResponseDelivery}. Cache misses and responses that require 27 * refresh are enqueued on the specified network queue for processing 28 * by a {@link NetworkDispatcher}. 29 */ 30 @SuppressWarnings("rawtypes") 31 public class CacheDispatcher extends Thread { 32 33 private static final boolean DEBUG = VolleyLog.DEBUG; 34 35 /** The queue of requests coming in for triage. */ 36 private final BlockingQueue<Request> mCacheQueue; 37 38 /** The queue of requests going out to the network. */ 39 private final BlockingQueue<Request> mNetworkQueue; 40 41 /** The cache to read from. */ 42 private final Cache mCache; 43 44 /** For posting responses. */ 45 private final ResponseDelivery mDelivery; 46 47 /** Used for telling us to die. */ 48 private volatile boolean mQuit = false; 49 50 /** 51 * Creates a new cache triage dispatcher thread. You must call {@link #start()} 52 * in order to begin processing. 53 * 54 * @param cacheQueue Queue of incoming requests for triage 55 * @param networkQueue Queue to post requests that require network to 56 * @param cache Cache interface to use for resolution 57 * @param delivery Delivery interface to use for posting responses 58 */ CacheDispatcher( BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue, Cache cache, ResponseDelivery delivery)59 public CacheDispatcher( 60 BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue, 61 Cache cache, ResponseDelivery delivery) { 62 mCacheQueue = cacheQueue; 63 mNetworkQueue = networkQueue; 64 mCache = cache; 65 mDelivery = delivery; 66 } 67 68 /** 69 * Forces this dispatcher to quit immediately. If any requests are still in 70 * the queue, they are not guaranteed to be processed. 71 */ quit()72 public void quit() { 73 mQuit = true; 74 interrupt(); 75 } 76 77 @Override run()78 public void run() { 79 if (DEBUG) VolleyLog.v("start new dispatcher"); 80 81 // Make a blocking call to initialize the cache. 82 mCache.initialize(); 83 84 while (true) { 85 try { 86 // Get a request from the cache triage queue, blocking until 87 // at least one is available. 88 final Request request = mCacheQueue.take(); 89 request.addMarker("cache-queue-take"); 90 91 // If the request has been canceled, don't bother dispatching it. 92 if (request.isCanceled()) { 93 request.finish("cache-discard-canceled"); 94 continue; 95 } 96 97 // Attempt to retrieve this item from cache. 98 Cache.Entry entry = mCache.get(request.getCacheKey()); 99 if (entry == null) { 100 request.addMarker("cache-miss"); 101 // Cache miss; send off to the network dispatcher. 102 mNetworkQueue.put(request); 103 continue; 104 } 105 106 // If it is completely expired, just send it to the network. 107 if (entry.isExpired()) { 108 request.addMarker("cache-hit-expired"); 109 request.setCacheEntry(entry); 110 mNetworkQueue.put(request); 111 continue; 112 } 113 114 // We have a cache hit; parse its data for delivery back to the request. 115 request.addMarker("cache-hit"); 116 Response<?> response = request.parseNetworkResponse( 117 new NetworkResponse(entry.data)); 118 request.addMarker("cache-hit-parsed"); 119 120 if (!entry.refreshNeeded()) { 121 // Completely unexpired cache hit. Just deliver the response. 122 mDelivery.postResponse(request, response); 123 } else { 124 // Soft-expired cache hit. We can deliver the cached response, 125 // but we need to also send the request to the network for 126 // refreshing. 127 request.addMarker("cache-hit-refresh-needed"); 128 request.setCacheEntry(entry); 129 130 // Mark the response as intermediate. 131 response.intermediate = true; 132 133 // Post the intermediate response back to the user and have 134 // the delivery then forward the request along to the network. 135 mDelivery.postResponse(request, response, new Runnable() { 136 @Override 137 public void run() { 138 try { 139 mNetworkQueue.put(request); 140 } catch (InterruptedException e) { 141 // Not much we can do about this. 142 } 143 } 144 }); 145 } 146 147 } catch (InterruptedException e) { 148 // We may have been interrupted because it was time to quit. 149 if (mQuit) { 150 return; 151 } 152 continue; 153 } 154 } 155 } 156 } 157