1d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/* 2d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Copyright (C) 2011 The Android Open Source Project 3d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 4d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Licensed under the Apache License, Version 2.0 (the "License"); 5d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * you may not use this file except in compliance with the License. 6d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * You may obtain a copy of the License at 7d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 8d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * http://www.apache.org/licenses/LICENSE-2.0 9d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 10d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software 11d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS, 12d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * See the License for the specific language governing permissions and 14d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * limitations under the License. 15d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 16d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 17d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupackage com.android.volley; 18d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 1935d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrickimport android.annotation.TargetApi; 20cc3a9344202b3b3ee7e1a33b79591b3d11182354Evan Charltonimport android.net.TrafficStats; 21cc3a9344202b3b3ee7e1a33b79591b3d11182354Evan Charltonimport android.os.Build; 226772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queruimport android.os.Process; 23750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalevimport android.os.SystemClock; 246772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru 25d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.concurrent.BlockingQueue; 26d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 27d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/** 28d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Provides a thread for performing network dispatch from a queue of requests. 29d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 30d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Requests added to the specified queue are processed from the network via a 31d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * specified {@link Network} interface. Responses are committed to cache, if 32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * eligible, using a specified {@link Cache} interface. Valid responses and 33d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * errors are posted back to the caller via a {@link ResponseDelivery}. 34d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupublic class NetworkDispatcher extends Thread { 36d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** The queue of requests to service. */ 3735d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick private final BlockingQueue<Request<?>> mQueue; 38d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** The network interface for processing requests. */ 39d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final Network mNetwork; 40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** The cache to write to. */ 41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final Cache mCache; 42d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** For posting responses and errors. */ 43d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final ResponseDelivery mDelivery; 44d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Used for telling us to die. */ 45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private volatile boolean mQuit = false; 46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 47d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Creates a new network dispatcher thread. You must call {@link #start()} 49d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * in order to begin processing. 50d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 51d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param queue Queue of incoming requests for triage 52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param network Network interface to use for performing requests 53d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param cache Cache interface to use for writing responses to cache 54d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param delivery Delivery interface to use for posting responses 55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 5635d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick public NetworkDispatcher(BlockingQueue<Request<?>> queue, 57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Network network, Cache cache, 58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru ResponseDelivery delivery) { 59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mQueue = queue; 60d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mNetwork = network; 61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mCache = cache; 62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mDelivery = delivery; 63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 65d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 66d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Forces this dispatcher to quit immediately. If any requests are still in 67d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * the queue, they are not guaranteed to be processed. 68d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 69d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public void quit() { 70d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mQuit = true; 71d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru interrupt(); 72d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 73d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 7435d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 7535d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick private void addTrafficStatsTag(Request<?> request) { 7635d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick // Tag the request (if API >= 14) 7735d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 7835d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick TrafficStats.setThreadStatsTag(request.getTrafficStatsTag()); 7935d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick } 8035d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick } 8135d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick 82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public void run() { 846772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 85d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru while (true) { 86750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev long startTimeMs = SystemClock.elapsedRealtime(); 87f4c18d49913ddf4d43ad546e2b8c5f3d6ec7c88eFicus Kirkpatrick Request<?> request; 88d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 89d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Take a request from the queue. 90d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request = mQueue.take(); 91d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (InterruptedException e) { 92d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // We may have been interrupted because it was time to quit. 93d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (mQuit) { 94d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return; 95d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 96d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru continue; 97d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 98d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 99d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 100d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request.addMarker("network-queue-take"); 101d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 102d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // If the request was cancelled already, do not perform the 103d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // network request. 104d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (request.isCanceled()) { 105d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request.finish("network-discard-cancelled"); 106d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru continue; 107d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 108d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 10935d5cc345a7bc5c7391aeda3d3fce711c6376c7bFicus Kirkpatrick addTrafficStatsTag(request); 110cc3a9344202b3b3ee7e1a33b79591b3d11182354Evan Charlton 111d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Perform the network request. 112d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru NetworkResponse networkResponse = mNetwork.performRequest(request); 113d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request.addMarker("network-http-complete"); 114d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 115d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // If the server returned 304 AND we delivered a response already, 116d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // we're done -- don't deliver a second identical response. 117d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (networkResponse.notModified && request.hasHadResponseDelivered()) { 118d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request.finish("not-modified"); 119d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru continue; 120d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 121d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 122d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Parse the response here on the worker thread. 123d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Response<?> response = request.parseNetworkResponse(networkResponse); 124d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request.addMarker("network-parse-complete"); 125d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 126d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Write to cache if applicable. 127d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // TODO: Only update cache metadata instead of entire record for 304s. 128d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (request.shouldCache() && response.cacheEntry != null) { 129d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mCache.put(request.getCacheKey(), response.cacheEntry); 130d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request.addMarker("network-cache-written"); 131d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 132d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 133d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Post the response back. 134d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru request.markDelivered(); 135d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mDelivery.postResponse(request, response); 136d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (VolleyError volleyError) { 137750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); 138d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru parseAndDeliverNetworkError(request, volleyError); 139d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (Exception e) { 140c0d371cf523ec7db27e0b619c174295c4c9e1ac0Evan Charlton VolleyLog.e(e, "Unhandled exception %s", e.toString()); 141750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev VolleyError volleyError = new VolleyError(e); 142750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); 143750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev mDelivery.postError(request, volleyError); 144d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 145d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 146d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 147d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 148d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) { 149d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru error = request.parseNetworkError(error); 150d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mDelivery.postError(request, error); 151d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 152d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru} 153