aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/java/com/android/volley/NetworkDispatcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/com/android/volley/NetworkDispatcher.java')
-rw-r--r--core/src/main/java/com/android/volley/NetworkDispatcher.java177
1 files changed, 177 insertions, 0 deletions
diff --git a/core/src/main/java/com/android/volley/NetworkDispatcher.java b/core/src/main/java/com/android/volley/NetworkDispatcher.java
new file mode 100644
index 0000000..06057c3
--- /dev/null
+++ b/core/src/main/java/com/android/volley/NetworkDispatcher.java
@@ -0,0 +1,177 @@
+/*
+ * 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.android.volley;
+
+import android.annotation.TargetApi;
+import android.net.TrafficStats;
+import android.os.Build;
+import android.os.Process;
+import android.os.SystemClock;
+import androidx.annotation.VisibleForTesting;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * Provides a thread for performing network dispatch from a queue of requests.
+ *
+ * <p>Requests added to the specified queue are processed from the network via a specified {@link
+ * Network} interface. Responses are committed to cache, if eligible, using a specified {@link
+ * Cache} interface. Valid responses and errors are posted back to the caller via a {@link
+ * ResponseDelivery}.
+ */
+public class NetworkDispatcher extends Thread {
+
+ /** The queue of requests to service. */
+ private final BlockingQueue<Request<?>> mQueue;
+ /** The network interface for processing requests. */
+ private final Network mNetwork;
+ /** The cache to write to. */
+ private final Cache mCache;
+ /** For posting responses and errors. */
+ private final ResponseDelivery mDelivery;
+ /** Used for telling us to die. */
+ private volatile boolean mQuit = false;
+
+ /**
+ * Creates a new network dispatcher thread. You must call {@link #start()} in order to begin
+ * processing.
+ *
+ * @param queue Queue of incoming requests for triage
+ * @param network Network interface to use for performing requests
+ * @param cache Cache interface to use for writing responses to cache
+ * @param delivery Delivery interface to use for posting responses
+ */
+ public NetworkDispatcher(
+ BlockingQueue<Request<?>> queue,
+ Network network,
+ Cache cache,
+ ResponseDelivery delivery) {
+ mQueue = queue;
+ mNetwork = network;
+ 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();
+ }
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ private void addTrafficStatsTag(Request<?> request) {
+ // Tag the request (if API >= 14)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
+ }
+ }
+
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ while (true) {
+ try {
+ processRequest();
+ } catch (InterruptedException e) {
+ // We may have been interrupted because it was time to quit.
+ if (mQuit) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ VolleyLog.e(
+ "Ignoring spurious interrupt of NetworkDispatcher thread; "
+ + "use quit() to terminate it");
+ }
+ }
+ }
+
+ // Extracted to its own method to ensure locals have a constrained liveness scope by the GC.
+ // This is needed to avoid keeping previous request references alive for an indeterminate amount
+ // of time. Update consumer-proguard-rules.pro when modifying this. See also
+ // https://github.com/google/volley/issues/114
+ private void processRequest() throws InterruptedException {
+ // Take a request from the queue.
+ Request<?> request = mQueue.take();
+ processRequest(request);
+ }
+
+ @VisibleForTesting
+ void processRequest(Request<?> request) {
+ long startTimeMs = SystemClock.elapsedRealtime();
+ request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
+ try {
+ request.addMarker("network-queue-take");
+
+ // If the request was cancelled already, do not perform the
+ // network request.
+ if (request.isCanceled()) {
+ request.finish("network-discard-cancelled");
+ request.notifyListenerResponseNotUsable();
+ return;
+ }
+
+ addTrafficStatsTag(request);
+
+ // Perform the network request.
+ NetworkResponse networkResponse = mNetwork.performRequest(request);
+ request.addMarker("network-http-complete");
+
+ // If the server returned 304 AND we delivered a response already,
+ // we're done -- don't deliver a second identical response.
+ if (networkResponse.notModified && request.hasHadResponseDelivered()) {
+ request.finish("not-modified");
+ request.notifyListenerResponseNotUsable();
+ return;
+ }
+
+ // Parse the response here on the worker thread.
+ Response<?> response = request.parseNetworkResponse(networkResponse);
+ request.addMarker("network-parse-complete");
+
+ // Write to cache if applicable.
+ // TODO: Only update cache metadata instead of entire record for 304s.
+ if (request.shouldCache() && response.cacheEntry != null) {
+ mCache.put(request.getCacheKey(), response.cacheEntry);
+ request.addMarker("network-cache-written");
+ }
+
+ // Post the response back.
+ request.markDelivered();
+ mDelivery.postResponse(request, response);
+ request.notifyListenerResponseReceived(response);
+ } catch (VolleyError volleyError) {
+ volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
+ parseAndDeliverNetworkError(request, volleyError);
+ request.notifyListenerResponseNotUsable();
+ } catch (Exception e) {
+ VolleyLog.e(e, "Unhandled exception %s", e.toString());
+ VolleyError volleyError = new VolleyError(e);
+ volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
+ mDelivery.postError(request, volleyError);
+ request.notifyListenerResponseNotUsable();
+ } finally {
+ request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED);
+ }
+ }
+
+ private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
+ error = request.parseNetworkError(error);
+ mDelivery.postError(request, error);
+ }
+}