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.annotation.TargetApi;
     20 import android.net.TrafficStats;
     21 import android.os.Build;
     22 import android.os.Process;
     23 import android.os.SystemClock;
     24 
     25 import java.util.concurrent.BlockingQueue;
     26 
     27 /**
     28  * Provides a thread for performing network dispatch from a queue of requests.
     29  *
     30  * Requests added to the specified queue are processed from the network via a
     31  * specified {@link Network} interface. Responses are committed to cache, if
     32  * eligible, using a specified {@link Cache} interface. Valid responses and
     33  * errors are posted back to the caller via a {@link ResponseDelivery}.
     34  */
     35 public class NetworkDispatcher extends Thread {
     36 
     37     /** The queue of requests to service. */
     38     private final BlockingQueue<Request<?>> mQueue;
     39     /** The network interface for processing requests. */
     40     private final Network mNetwork;
     41     /** The cache to write to. */
     42     private final Cache mCache;
     43     /** For posting responses and errors. */
     44     private final ResponseDelivery mDelivery;
     45     /** Used for telling us to die. */
     46     private volatile boolean mQuit = false;
     47 
     48     /**
     49      * Creates a new network dispatcher thread.  You must call {@link #start()}
     50      * in order to begin processing.
     51      *
     52      * @param queue Queue of incoming requests for triage
     53      * @param network Network interface to use for performing requests
     54      * @param cache Cache interface to use for writing responses to cache
     55      * @param delivery Delivery interface to use for posting responses
     56      */
     57     public NetworkDispatcher(BlockingQueue<Request<?>> queue,
     58             Network network, Cache cache, ResponseDelivery delivery) {
     59         mQueue = queue;
     60         mNetwork = network;
     61         mCache = cache;
     62         mDelivery = delivery;
     63     }
     64 
     65     /**
     66      * Forces this dispatcher to quit immediately.  If any requests are still in
     67      * the queue, they are not guaranteed to be processed.
     68      */
     69     public void quit() {
     70         mQuit = true;
     71         interrupt();
     72     }
     73 
     74     @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
     75     private void addTrafficStatsTag(Request<?> request) {
     76         // Tag the request (if API >= 14)
     77         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
     78             TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
     79         }
     80     }
     81 
     82     @Override
     83     public void run() {
     84         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
     85         while (true) {
     86             try {
     87                 processRequest();
     88             } catch (InterruptedException e) {
     89                 // We may have been interrupted because it was time to quit.
     90                 if (mQuit) {
     91                     return;
     92                 }
     93             }
     94         }
     95     }
     96 
     97     // Extracted to its own method to ensure locals have a constrained liveness scope by the GC.
     98     // This is needed to avoid keeping previous request references alive for an indeterminate amount
     99     // of time. Update consumer-proguard-rules.pro when modifying this. See also
    100     // https://github.com/google/volley/issues/114
    101     private void processRequest() throws InterruptedException {
    102         long startTimeMs = SystemClock.elapsedRealtime();
    103         // Take a request from the queue.
    104         Request<?> request = mQueue.take();
    105 
    106         try {
    107             request.addMarker("network-queue-take");
    108 
    109             // If the request was cancelled already, do not perform the
    110             // network request.
    111             if (request.isCanceled()) {
    112                 request.finish("network-discard-cancelled");
    113                 request.notifyListenerResponseNotUsable();
    114                 return;
    115             }
    116 
    117             addTrafficStatsTag(request);
    118 
    119             // Perform the network request.
    120             NetworkResponse networkResponse = mNetwork.performRequest(request);
    121             request.addMarker("network-http-complete");
    122 
    123             // If the server returned 304 AND we delivered a response already,
    124             // we're done -- don't deliver a second identical response.
    125             if (networkResponse.notModified && request.hasHadResponseDelivered()) {
    126                 request.finish("not-modified");
    127                 request.notifyListenerResponseNotUsable();
    128                 return;
    129             }
    130 
    131             // Parse the response here on the worker thread.
    132             Response<?> response = request.parseNetworkResponse(networkResponse);
    133             request.addMarker("network-parse-complete");
    134 
    135             // Write to cache if applicable.
    136             // TODO: Only update cache metadata instead of entire record for 304s.
    137             if (request.shouldCache() && response.cacheEntry != null) {
    138                 mCache.put(request.getCacheKey(), response.cacheEntry);
    139                 request.addMarker("network-cache-written");
    140             }
    141 
    142             // Post the response back.
    143             request.markDelivered();
    144             mDelivery.postResponse(request, response);
    145             request.notifyListenerResponseReceived(response);
    146         } catch (VolleyError volleyError) {
    147             volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    148             parseAndDeliverNetworkError(request, volleyError);
    149             request.notifyListenerResponseNotUsable();
    150         } catch (Exception e) {
    151             VolleyLog.e(e, "Unhandled exception %s", e.toString());
    152             VolleyError volleyError = new VolleyError(e);
    153             volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    154             mDelivery.postError(request, volleyError);
    155             request.notifyListenerResponseNotUsable();
    156         }
    157     }
    158 
    159     private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
    160         error = request.parseNetworkError(error);
    161         mDelivery.postError(request, error);
    162     }
    163 }
    164