/*
* Copyright (C) 2014 Hari Krishna Dulipudi
*
* 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.Handler;
import android.os.Looper;
import android.os.SystemClock;
import com.android.volley.error.VolleyError;
/**
* A request tickle for single requests.
*
* Calling {@link #add(Request)} will add the request to request tickle and {@link #start()},
* will give a {@link NetworkResponse}. The listeners in the request will also be notified.
*/
public class RequestTickle {
private Request<?> mRequest;
/** Cache interface for retrieving and storing response. */
private final Cache mCache;
/** Network interface for performing requests. */
private final Network mNetwork;
/** Response delivery mechanism. */
private final ResponseDelivery mDelivery;
private Response<?> response;
private VolleyError error;
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestTickle(Cache cache, Network network,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDelivery = delivery;
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
*/
public RequestTickle(Cache cache, Network network) {
this(cache, network,new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
public <T> Request<T> add(Request<T> request) {
this.mRequest = request;
return request;
}
/**
* Cancel the request.
*/
public void cancel() {
if(null == mRequest){
return;
}
mRequest.cancel();
}
/**
* Gets the {@link Cache} instance being used.
*/
public Cache getCache() {
return mCache;
}
/**
* Starts the request and return {@link NetworkResponse} or null.
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public NetworkResponse start() {
if(null == mRequest){
return null;
}
NetworkResponse networkResponse = null;
long startTimeMs = SystemClock.elapsedRealtime();
try {
mRequest.addMarker("network-queue-take");
// If the request was cancelled already, do not perform the
// network request.
if (mRequest.isCanceled()) {
mRequest.finish("network-discard-cancelled");
return null;
}
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(mRequest.getTrafficStatsTag());
}
// Perform the network request.
networkResponse = mNetwork.performRequest(mRequest);
//mRequest.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 && mRequest.hasHadResponseDelivered()) {
mRequest.finish("not-modified");
return networkResponse;
}
// Parse the response here on the worker thread.
response = mRequest.parseNetworkResponse(networkResponse);
mRequest.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
if (mCache != null && mRequest.shouldCache() && response.cacheEntry != null) {
mCache.put(mRequest.getCacheKey(), response.cacheEntry);
mRequest.addMarker("network-cache-written");
}
// Post the response back.
mRequest.markDelivered();
mDelivery.postResponse(mRequest, response);
} catch (VolleyError volleyError) {
networkResponse = volleyError.networkResponse;
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(mRequest, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(mRequest, volleyError);
}
if(null == networkResponse){
networkResponse = new NetworkResponse(0, null, null, false);
}
return networkResponse;
}
public Response<?> getResponse() {
return response;
}
public VolleyError getError() {
return error;
}
private void parseAndDeliverNetworkError(Request<?> request, VolleyError volleyError) {
error = request.parseNetworkError(volleyError);
mDelivery.postError(request, error);
}
}