• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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