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