diff options
Diffstat (limited to 'core/src/main/java/com/android/volley/NetworkDispatcher.java')
-rw-r--r-- | core/src/main/java/com/android/volley/NetworkDispatcher.java | 177 |
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); + } +} |