content: /*
   * Copyright (C) 2011 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.cjj.volley;
  
  import android.os.Process;
  
  import java.util.concurrent.BlockingQueue;
  
  /**
   * Provides a thread for performing cache triage on a queue of requests.
   *
   * Requests added to the specified cache queue are resolved from cache.
   * Any deliverable response is posted back to the caller via a
   * {@link ResponseDelivery}.  Cache misses and responses that require
   * refresh are enqueued on the specified network queue for processing
   * by a {@link NetworkDispatcher}.
   */
  public class CacheDispatcher extends Thread {
  
      private static final boolean DEBUG = VolleyLog.DEBUG;
  
      /** 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;
  
      /** The cache to read from. */
      private final Cache mCache;
  
      /** For posting responses. */
      private final ResponseDelivery mDelivery;
  
      /** Used for telling us to die. */
      private volatile boolean mQuit = false;
  
      /**
       * Creates a new cache triage dispatcher thread.  You must call {@link #start()}
       * in order to begin processing.
       *
       * @param cacheQueue Queue of incoming requests for triage
       * @param networkQueue Queue to post requests that require network to
       * @param cache Cache interface to use for resolution
       * @param delivery Delivery interface to use for posting responses
       */
      public CacheDispatcher(
              BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
              Cache cache, ResponseDelivery delivery) {
          mCacheQueue = cacheQueue;
          mNetworkQueue = networkQueue;
          mCache = cache;
          mDelivery = delivery;
      }
  
      /**
       * Forces this dispatcher to quit immediately.  If any requests are still in
       * the queue, they are not guaranteed to be processed.
       */
      public void quit() {
          mQuit = true;
          interrupt();
      }
  
      @Override
      public void run() {
          if (DEBUG) VolleyLog.v("start new dispatcher");
          Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  
          // Make a blocking call to initialize the cache.
          mCache.initialize();
  
          while (true) {
              try {
                  // Get a request from the cache triage queue, blocking until
                  // at least one is available.
                  final Request<?> request = mCacheQueue.take();
                  request.addMarker("cache-queue-take");
  
                  // If the request has been canceled, don't bother dispatching it.
                  if (request.isCanceled()) {
                      request.finish("cache-discard-canceled");
                      continue;
                  }
  
                  // Attempt to retrieve this item from cache.
                  Cache.Entry entry = mCache.get(request.getCacheKey());
                  if (entry == null) {
                      request.addMarker("cache-miss");
                      // Cache miss; send off to the network dispatcher.
                      mNetworkQueue.put(request);
                      continue;
                  }
  
                  // If it is completely expired, just send it to the network.
                  if (entry.isExpired()) {
                      request.addMarker("cache-hit-expired");
                      request.setCacheEntry(entry);
                      mNetworkQueue.put(request);
                      continue;
                  }
  
                  // We have a cache hit; parse its data for delivery back to the request.
                  request.addMarker("cache-hit");
                  Response<?> response = request.parseNetworkResponse(
                          new NetworkResponse(entry.data, entry.responseHeaders));
                  request.addMarker("cache-hit-parsed");
  
                  if (!entry.refreshNeeded()) {
                      // Completely unexpired cache hit. Just deliver the response.
                      mDelivery.postResponse(request, response);
                  } else {
                      // Soft-expired cache hit. We can deliver the cached response,
                      // but we need to also send the request to the network for
                      // refreshing.
                      request.addMarker("cache-hit-refresh-needed");
                      request.setCacheEntry(entry);
  
                      // Mark the response as intermediate.
                      response.intermediate = true;
  
                      // Post the intermediate response back to the user and have
                      // the delivery then forward the request along to the 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;
              }
          }
      }
  }
  