• 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 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     /** The queue of requests to service. */
37     private final BlockingQueue<Request<?>> mQueue;
38     /** The network interface for processing requests. */
39     private final Network mNetwork;
40     /** The cache to write to. */
41     private final Cache mCache;
42     /** For posting responses and errors. */
43     private final ResponseDelivery mDelivery;
44     /** Used for telling us to die. */
45     private volatile boolean mQuit = false;
46 
47     /**
48      * Creates a new network dispatcher thread.  You must call {@link #start()}
49      * in order to begin processing.
50      *
51      * @param queue Queue of incoming requests for triage
52      * @param network Network interface to use for performing requests
53      * @param cache Cache interface to use for writing responses to cache
54      * @param delivery Delivery interface to use for posting responses
55      */
NetworkDispatcher(BlockingQueue<Request<?>> queue, Network network, Cache cache, ResponseDelivery delivery)56     public NetworkDispatcher(BlockingQueue<Request<?>> queue,
57             Network network, Cache cache,
58             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      */
quit()69     public void quit() {
70         mQuit = true;
71         interrupt();
72     }
73 
74     @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
addTrafficStatsTag(Request<?> request)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
run()83     public void run() {
84         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
85         while (true) {
86             long startTimeMs = SystemClock.elapsedRealtime();
87             Request<?> request;
88             try {
89                 // Take a request from the queue.
90                 request = mQueue.take();
91             } catch (InterruptedException e) {
92                 // We may have been interrupted because it was time to quit.
93                 if (mQuit) {
94                     return;
95                 }
96                 continue;
97             }
98 
99             try {
100                 request.addMarker("network-queue-take");
101 
102                 // If the request was cancelled already, do not perform the
103                 // network request.
104                 if (request.isCanceled()) {
105                     request.finish("network-discard-cancelled");
106                     continue;
107                 }
108 
109                 addTrafficStatsTag(request);
110 
111                 // Perform the network request.
112                 NetworkResponse networkResponse = mNetwork.performRequest(request);
113                 request.addMarker("network-http-complete");
114 
115                 // If the server returned 304 AND we delivered a response already,
116                 // we're done -- don't deliver a second identical response.
117                 if (networkResponse.notModified && request.hasHadResponseDelivered()) {
118                     request.finish("not-modified");
119                     continue;
120                 }
121 
122                 // Parse the response here on the worker thread.
123                 Response<?> response = request.parseNetworkResponse(networkResponse);
124                 request.addMarker("network-parse-complete");
125 
126                 // Write to cache if applicable.
127                 // TODO: Only update cache metadata instead of entire record for 304s.
128                 if (request.shouldCache() && response.cacheEntry != null) {
129                     mCache.put(request.getCacheKey(), response.cacheEntry);
130                     request.addMarker("network-cache-written");
131                 }
132 
133                 // Post the response back.
134                 request.markDelivered();
135                 mDelivery.postResponse(request, response);
136             } catch (VolleyError volleyError) {
137                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
138                 parseAndDeliverNetworkError(request, volleyError);
139             } catch (Exception e) {
140                 VolleyLog.e(e, "Unhandled exception %s", e.toString());
141                 VolleyError volleyError = new VolleyError(e);
142                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
143                 mDelivery.postError(request, volleyError);
144             }
145         }
146     }
147 
parseAndDeliverNetworkError(Request<?> request, VolleyError error)148     private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
149         error = request.parseNetworkError(error);
150         mDelivery.postError(request, error);
151     }
152 }
153