/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.imagepipeline.backends.volley;
import android.os.SystemClock;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.RequestQueue.RequestFilter;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.facebook.imagepipeline.image.EncodedImage;
import com.facebook.imagepipeline.producers.BaseNetworkFetcher;
import com.facebook.imagepipeline.producers.BaseProducerContextCallbacks;
import com.facebook.imagepipeline.producers.Consumer;
import com.facebook.imagepipeline.producers.FetchState;
import com.facebook.imagepipeline.producers.ProducerContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* Network fetcher that uses Volley as its backend. The request being made will bypass Volley's
* cache.
* <p>
* Volley does not allow access to a {@link InputStream}. Therefore, responses will be passed
* along as complete byte arrays, which will not allow for progressive JPEG streaming.
*/
public class VolleyNetworkFetcher extends
BaseNetworkFetcher<VolleyNetworkFetcher.VolleyNetworkFetchState> {
public static class VolleyNetworkFetchState extends FetchState {
long submitTime;
long responseTime;
long fetchCompleteTime;
public VolleyNetworkFetchState(
Consumer<EncodedImage> consumer,
ProducerContext producerContext) {
super(consumer, producerContext);
}
}
private static final String QUEUE_TIME = "queue_time";
private static final String FETCH_TIME = "fetch_time";
private static final String TOTAL_TIME = "total_time";
private static final String IMAGE_SIZE = "image_size";
private final RequestQueue mRequestQueue;
/**
* @param requestQueue The Volley {@link RequestQueue} to use
*/
public VolleyNetworkFetcher(RequestQueue requestQueue) {
mRequestQueue = requestQueue;
}
@Override
public VolleyNetworkFetchState createFetchState(
Consumer<EncodedImage> consumer,
ProducerContext context) {
return new VolleyNetworkFetchState(consumer, context);
}
@Override
public void fetch(final VolleyNetworkFetchState fetchState, final Callback callback) {
fetchState.submitTime = SystemClock.elapsedRealtime();
final RawRequest request = new RawRequest(
fetchState.getUri().toString(),
new Response.Listener<byte[]>() {
@Override
public void onResponse(byte[] bytes) {
fetchState.responseTime = SystemClock.uptimeMillis();
try {
InputStream is = new ByteArrayInputStream(bytes);
callback.onResponse(is, bytes.length);
} catch (IOException e) {
callback.onFailure(e);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
callback.onFailure(volleyError);
}
});
fetchState.getContext().addCallbacks(
new BaseProducerContextCallbacks() {
@Override
public void onCancellationRequested() {
mRequestQueue.cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> candidate) {
return candidate != null && request.getSequence() == candidate.getSequence();
}
});
}
});
mRequestQueue.add(request);
}
@Override
public void onFetchCompletion(VolleyNetworkFetchState fetchState, int byteSize) {
fetchState.fetchCompleteTime = SystemClock.elapsedRealtime();
}
@Override
public Map<String, String> getExtraMap(VolleyNetworkFetchState fetchState, int byteSize) {
Map<String, String> extraMap = new HashMap<>(4);
extraMap.put(QUEUE_TIME, Long.toString(fetchState.responseTime - fetchState.submitTime));
extraMap.put(FETCH_TIME, Long.toString(fetchState.fetchCompleteTime - fetchState.responseTime));
extraMap.put(TOTAL_TIME, Long.toString(fetchState.fetchCompleteTime - fetchState.submitTime));
extraMap.put(IMAGE_SIZE, Integer.toString(byteSize));
return extraMap;
}
}