Skip to content

Instantly share code, notes, and snippets.

@billmote
Last active August 29, 2015 14:19
Show Gist options
  • Save billmote/2911e438a3a855379f06 to your computer and use it in GitHub Desktop.
Save billmote/2911e438a3a855379f06 to your computer and use it in GitHub Desktop.
A cache for OkHttp calls that implements the Builder Pattern or can be called statically.
import android.content.Context;
import com.androidfu.nowplaying.app.util.Log;
import com.androidfu.nowplaying.app.util.Preconditions;
import com.jakewharton.byteunits.DecimalByteUnit;
import com.squareup.okhttp.Cache;
import com.squareup.okhttp.OkHttpClient;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import hugo.weaving.DebugLog;
/**
*
*/
@DebugLog
public final class CachingOkHttpClient {
private static final String TAG = "CachingOkHttpClient";
private static final int DEFAULT_CONNECT_TIMEOUT = 10;
private static final int DEFAULT_WRITE_TIMEOUT = 10;
private static final int DEFAULT_READ_TIMEOUT = 10;
private static final long DEFAULT_DISK_CACHE_SIZE = DecimalByteUnit.MEGABYTES.toBytes(10);
private static volatile CachingOkHttpClient instance = null;
private final OkHttpClient client;
private Cache cache;
private CachingOkHttpClient(final Builder builder) {
this.client = new OkHttpClient();
this.client.setConnectTimeout(builder.connectTimeout, TimeUnit.SECONDS);
this.client.setWriteTimeout(builder.writeTimeout, TimeUnit.SECONDS);
this.client.setReadTimeout(builder.readTimeout, TimeUnit.SECONDS);
try {
if (cache == null) {
Log.i(TAG, String.format("Creating response cache of %d bytes ...", DEFAULT_DISK_CACHE_SIZE));
cache = new Cache(builder.directory, builder.diskCacheSize);
Log.i(TAG, "success.");
}
} catch (IOException e) {
Log.e(TAG, String.format("Error creating response cache: %s", e.getMessage()), e);
}
if (cache != null) {
client.setCache(cache);
}
}
/*
A static alternative to the Builder().
*/
public static CachingOkHttpClient with(Context context) {
if (instance == null) {
synchronized (CachingOkHttpClient.class) {
if (instance == null) {
instance = new Builder(context).build();
}
}
}
return instance;
}
public OkHttpClient getClient() {
return client.clone(); //Shallow copy of OkHttpClient. All downstream clients share the same cache and each client can set their own interceptors etc.
}
public static class Builder {
private final Context context;
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private int writeTimeout = DEFAULT_WRITE_TIMEOUT;
private int readTimeout = DEFAULT_READ_TIMEOUT;
private long diskCacheSize = DEFAULT_DISK_CACHE_SIZE;
private File directory;
public Builder(Context context) {
Preconditions.checkNotNull(context, "context != null");
this.context = context;
}
public Builder connectTimeout(int connectTimeout) {
Preconditions.checkArgument(connectTimeout > 0, "connectTimeout must be > 0");
this.connectTimeout = connectTimeout;
return this;
}
public Builder writeTimeout(int writeTimeout) {
Preconditions.checkArgument(writeTimeout > 0, "writeTimeout must be > 0");
this.writeTimeout = writeTimeout;
return this;
}
public Builder readTimeout(int readTimeout) {
Preconditions.checkArgument(readTimeout > 0, "readTimeout must be > 0");
this.readTimeout = readTimeout;
return this;
}
public Builder diskCacheSize(long diskCacheSize) {
Preconditions.checkArgument(diskCacheSize > 0, "diskSize must be > 0");
this.diskCacheSize = diskCacheSize;
return this;
}
public Builder file(File directory) {
Preconditions.checkNotNull(directory, "directory != null");
this.directory = directory;
return this;
}
public CachingOkHttpClient build() {
ensureSaneDefaults();
return new CachingOkHttpClient(this);
}
private void ensureSaneDefaults() {
if (directory == null) {
directory = new File(context.getCacheDir(), "http");
}
}
}
}
import android.text.TextUtils;
/**
* Created by billmote on 4/26/15.
*/
public final class Preconditions {
private Preconditions() {
throw new AssertionError("No instances.");
}
public static void checkArgument(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
}
public static void checkState(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
}
public static <T> T checkNotNull(T reference, String name) {
if (reference == null) {
throw new NullPointerException(name + " must not be null");
}
return reference;
}
public static <T extends CharSequence> T checkNotBlank(T value, String name) {
if (TextUtils.isEmpty(value)) {
throw new IllegalArgumentException(name + " must not be blank");
}
return value;
}
}
import android.app.Application;
import com.androidfu.nowplaying.app.BuildConfig;
import com.androidfu.nowplaying.app.model.movies.Movie;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.squareup.okhttp.OkHttpClient;
import java.lang.reflect.Type;
import hugo.weaving.DebugLog;
import retrofit.RestAdapter;
import retrofit.client.OkClient;
import retrofit.converter.GsonConverter;
/**
* Created by billmote on 9/7/14.
*/
@DebugLog
public class RestClient {
private ApiService apiService;
public RestClient(Application context, String endpoint, boolean enableLogging) {
OkHttpClient client = new CachingOkHttpClient.Builder(context).build().getClient();
Gson gsonBuilder = new GsonBuilder()
.registerTypeAdapterFactory(new ItemTypeAdapterFactory())
/*
Rotten Tomatoes returns an int when there's a valid value for runtime, but they
return an empty string when the value is null. Because they return different
JSON primitive types we have to create a custom TypeAdapter for our Movie.class.
*/
.registerTypeAdapter(Movie.class, new JsonDeserializer<Movie>() {
@Override
public Movie deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2) throws JsonParseException {
JsonObject movieObject = arg0.getAsJsonObject();
Gson gson = new Gson();
Movie movie = gson.fromJson(arg0, Movie.class);
int runtime;
try {
runtime = movieObject.get("runtime").getAsInt();
} catch (Exception e) {
runtime = 0;
}
movie.setMovieRuntime(runtime);
return movie;
}
})
.setDateFormat("yyyy-MM-dd")
.setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation()
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new OkClient(client))
.setLogLevel(BuildConfig.DEBUG && enableLogging ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
.setEndpoint(endpoint)
.setConverter(new GsonConverter(gsonBuilder, "UTF-8"))
.setRequestInterceptor(new SessionRequestInterceptor())
.build();
apiService = restAdapter.create(ApiService.class);
}
public ApiService getApiService() {
return apiService;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment