Home | History | Annotate | Download | only in volley
      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 android.os.Process;
     20 
     21 import java.util.concurrent.BlockingQueue;
     22 
     23 /**
     24  * Provides a thread for performing cache triage on a queue of requests.
     25  *
     26  * Requests added to the specified cache queue are resolved from cache.
     27  * Any deliverable response is posted back to the caller via a
     28  * {@link ResponseDelivery}.  Cache misses and responses that require
     29  * refresh are enqueued on the specified network queue for processing
     30  * by a {@link NetworkDispatcher}.
     31  */
     32 @SuppressWarnings("rawtypes")
     33 public class CacheDispatcher extends Thread {
     34 
     35     private static final boolean DEBUG = VolleyLog.DEBUG;
     36 
     37     /** The queue of requests coming in for triage. */
     38     private final BlockingQueue<Request> mCacheQueue;
     39 
     40     /** The queue of requests going out to the network. */
     41     private final BlockingQueue<Request> mNetworkQueue;
     42 
     43     /** The cache to read from. */
     44     private final Cache mCache;
     45 
     46     /** For posting responses. */
     47     private final ResponseDelivery mDelivery;
     48 
     49     /** Used for telling us to die. */
     50     private volatile boolean mQuit = false;
     51 
     52     /**
     53      * Creates a new cache triage dispatcher thread.  You must call {@link #start()}
     54      * in order to begin processing.
     55      *
     56      * @param cacheQueue Queue of incoming requests for triage
     57      * @param networkQueue Queue to post requests that require network to
     58      * @param cache Cache interface to use for resolution
     59      * @param delivery Delivery interface to use for posting responses
     60      */
     61     public CacheDispatcher(
     62             BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue,
     63             Cache cache, ResponseDelivery delivery) {
     64         mCacheQueue = cacheQueue;
     65         mNetworkQueue = networkQueue;
     66         mCache = cache;
     67         mDelivery = delivery;
     68     }
     69 
     70     /**
     71      * Forces this dispatcher to quit immediately.  If any requests are still in
     72      * the queue, they are not guaranteed to be processed.
     73      */
     74     public void quit() {
     75         mQuit = true;
     76         interrupt();
     77     }
     78 
     79     @Override
     80     public void run() {
     81         if (DEBUG) VolleyLog.v("start new dispatcher");
     82         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
     83 
     84         // Make a blocking call to initialize the cache.
     85         mCache.initialize();
     86 
     87         while (true) {
     88             try {
     89                 // Get a request from the cache triage queue, blocking until
     90                 // at least one is available.
     91                 final Request request = mCacheQueue.take();
     92                 request.addMarker("cache-queue-take");
     93 
     94                 // If the request has been canceled, don't bother dispatching it.
     95                 if (request.isCanceled()) {
     96                     request.finish("cache-discard-canceled");
     97                     continue;
     98                 }
     99 
    100                 // Attempt to retrieve this item from cache.
    101                 Cache.Entry entry = mCache.get(request.getCacheKey());
    102                 if (entry == null) {
    103                     request.addMarker("cache-miss");
    104                     // Cache miss; send off to the network dispatcher.
    105                     mNetworkQueue.put(request);
    106                     continue;
    107                 }
    108 
    109                 // If it is completely expired, just send it to the network.
    110                 if (entry.isExpired()) {
    111                     request.addMarker("cache-hit-expired");
    112                     request.setCacheEntry(entry);
    113                     mNetworkQueue.put(request);
    114                     continue;
    115                 }
    116 
    117                 // We have a cache hit; parse its data for delivery back to the request.
    118                 request.addMarker("cache-hit");
    119                 Response<?> response = request.parseNetworkResponse(
    120                         new NetworkResponse(entry.data, entry.responseHeaders));
    121                 request.addMarker("cache-hit-parsed");
    122 
    123                 if (!entry.refreshNeeded()) {
    124                     // Completely unexpired cache hit. Just deliver the response.
    125                     mDelivery.postResponse(request, response);
    126                 } else {
    127                     // Soft-expired cache hit. We can deliver the cached response,
    128                     // but we need to also send the request to the network for
    129                     // refreshing.
    130                     request.addMarker("cache-hit-refresh-needed");
    131                     request.setCacheEntry(entry);
    132 
    133                     // Mark the response as intermediate.
    134                     response.intermediate = true;
    135 
    136                     // Post the intermediate response back to the user and have
    137                     // the delivery then forward the request along to the network.
    138                     mDelivery.postResponse(request, response, new Runnable() {
    139                         @Override
    140                         public void run() {
    141                             try {
    142                                 mNetworkQueue.put(request);
    143                             } catch (InterruptedException e) {
    144                                 // Not much we can do about this.
    145                             }
    146                         }
    147                     });
    148                 }
    149 
    150             } catch (InterruptedException e) {
    151                 // We may have been interrupted because it was time to quit.
    152                 if (mQuit) {
    153                     return;
    154                 }
    155                 continue;
    156             }
    157         }
    158     }
    159 }
    160