package com.doumee.lib_coremodel.http.other;
|
|
import android.net.Uri;
|
import android.os.Looper;
|
import android.os.SystemClock;
|
|
import com.facebook.common.logging.FLog;
|
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.IOException;
|
import java.util.HashMap;
|
import java.util.Map;
|
import java.util.concurrent.Executor;
|
|
import okhttp3.CacheControl;
|
import okhttp3.Call;
|
import okhttp3.OkHttpClient;
|
import okhttp3.Request;
|
import okhttp3.Response;
|
import okhttp3.ResponseBody;
|
|
/**
|
* Created by android_ls on 2017/5/23.
|
*/
|
|
public class OkHttpNetworkFetcher extends
|
BaseNetworkFetcher<OkHttpNetworkFetcher.OkHttpNetworkFetchState> {
|
|
public static class OkHttpNetworkFetchState extends FetchState {
|
public long submitTime;
|
public long responseTime;
|
public long fetchCompleteTime;
|
|
public OkHttpNetworkFetchState(
|
Consumer<EncodedImage> consumer,
|
ProducerContext producerContext) {
|
super(consumer, producerContext);
|
}
|
}
|
|
private static final String TAG = "OkHttpNetworkFetchProducer";
|
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 Call.Factory mCallFactory;
|
|
private Executor mCancellationExecutor;
|
|
/**
|
* @param okHttpClient client to use
|
*/
|
public OkHttpNetworkFetcher(OkHttpClient okHttpClient) {
|
this(okHttpClient, okHttpClient.dispatcher().executorService());
|
}
|
|
/**
|
* @param callFactory custom {@link Call.Factory} for fetching image from the network
|
* @param cancellationExecutor executor on which fetching cancellation is performed if
|
* cancellation is requested from the UI Thread
|
*/
|
public OkHttpNetworkFetcher(Call.Factory callFactory, Executor cancellationExecutor) {
|
mCallFactory = callFactory;
|
mCancellationExecutor = cancellationExecutor;
|
}
|
|
@Override
|
public OkHttpNetworkFetchState createFetchState(
|
Consumer<EncodedImage> consumer,
|
ProducerContext context) {
|
return new OkHttpNetworkFetchState(consumer, context);
|
}
|
|
@Override
|
public void fetch(final OkHttpNetworkFetchState fetchState, final Callback callback) {
|
fetchState.submitTime = SystemClock.elapsedRealtime();
|
final Uri uri = fetchState.getUri();
|
|
try {
|
Request request = new Request.Builder()
|
.cacheControl(new CacheControl.Builder().noStore().build())
|
.url(uri.toString())
|
.get()
|
.build();
|
|
fetchWithRequest(fetchState, callback, request);
|
} catch (Exception e) {
|
// handle error while creating the request
|
callback.onFailure(e);
|
}
|
}
|
|
@Override
|
public void onFetchCompletion(OkHttpNetworkFetchState fetchState, int byteSize) {
|
fetchState.fetchCompleteTime = SystemClock.elapsedRealtime();
|
}
|
|
@Override
|
public Map<String, String> getExtraMap(OkHttpNetworkFetchState 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;
|
}
|
|
protected void fetchWithRequest(
|
final OkHttpNetworkFetchState fetchState,
|
final Callback callback,
|
final Request request) {
|
final Call call = mCallFactory.newCall(request);
|
|
fetchState.getContext().addCallbacks(
|
new BaseProducerContextCallbacks() {
|
@Override
|
public void onCancellationRequested() {
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
call.cancel();
|
} else {
|
mCancellationExecutor.execute(new Runnable() {
|
@Override
|
public void run() {
|
call.cancel();
|
}
|
});
|
}
|
}
|
});
|
|
call.enqueue(
|
new okhttp3.Callback() {
|
@Override
|
public void onResponse(Call call, Response response) throws IOException {
|
fetchState.responseTime = SystemClock.elapsedRealtime();
|
final ResponseBody body = response.body();
|
try {
|
if (!response.isSuccessful()) {
|
handleException(
|
call,
|
new IOException("Unexpected HTTP code " + response),
|
callback);
|
return;
|
}
|
|
long contentLength = body != null ? body.contentLength() : 0;
|
if (contentLength < 0) {
|
contentLength = 0;
|
}
|
callback.onResponse(body != null ? body.byteStream() : null, (int) contentLength);
|
} catch (Exception e) {
|
handleException(call, e, callback);
|
} finally {
|
try {
|
if(body != null) {
|
body.close();
|
}
|
} catch (Exception e) {
|
FLog.w(TAG, "Exception when closing response body", e);
|
}
|
}
|
}
|
|
@Override
|
public void onFailure(Call call, IOException e) {
|
handleException(call, e, callback);
|
}
|
});
|
}
|
|
/**
|
* Handles exceptions.
|
*
|
* <p> OkHttp notifies callers of cancellations via an IOException. If IOException is caught
|
* after request cancellation, then the exception is interpreted as successful cancellation
|
* and onCancellation is called. Otherwise onFailure is called.
|
*/
|
private void handleException(final Call call, final Exception e, final Callback callback) {
|
if (call.isCanceled()) {
|
callback.onCancellation();
|
} else {
|
callback.onFailure(e);
|
}
|
}
|
|
}
|