How to configure the Http Cache when using Volley with OkHttp? - android

I want to try Volley combining with OkHttp but Volley cache system and OkHttp both rely on the HTTP cache as defined in the HTTP specification. So how can be disabled the cache of OkHttp for keeping one copy of HTTP cache?
EDIT: what I have done
public class VolleyUtil {
// http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/
private volatile static RequestQueue sRequestQueue;
/** get the single instance of RequestQueue **/
public static RequestQueue getQueue(Context context) {
if (sRequestQueue == null) {
synchronized (VolleyUtil.class) {
if (sRequestQueue == null) {
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new StethoInterceptor());
client.setCache(null);
sRequestQueue = Volley.newRequestQueue(context.getApplicationContext(), new OkHttpStack(client));
VolleyLog.DEBUG = true;
}
}
}
return sRequestQueue;
}
}
Which OkHttpClient is referenced from https://gist.github.com/bryanstern/4e8f1cb5a8e14c202750

OkHttp is a kind of HTTP client like HttpUrlConnection which implements HTTP cache, we can disable the cache of OkHttp like below:
OkHttpClient client = new OkHttpClient();
client.setCache(null);
Then, we can keep one copy of HTTP cache maintained by Volley.
IMPROVED:
I'd like to try to answer Sotti's questions.
1 I would like to know what is a good cache setup when using Volley and OkHttp.
In my project, i'm using one Volley requestQueue instance across all of restful APIs, and OkHttp worked as the transport layer for Volley like below.
public class VolleyUtil {
// http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/
private volatile static RequestQueue sRequestQueue;
/** get the single instance of RequestQueue **/
public static RequestQueue getQueue(Context context) {
if (sRequestQueue == null) {
synchronized (VolleyUtil.class) {
if (sRequestQueue == null) {
OkHttpClient client = new OkHttpClient();
client.setCache(null);
sRequestQueue = Volley.newRequestQueue(context.getApplicationContext(), new OkHttpStack(client));
VolleyLog.DEBUG = true;
}
}
}
return sRequestQueue;
}}
2 Should we rely on Volley or on the OkHttp cache?
Yes, i'm using Volley cache for my HTTP Cache instead of OkHttp Cache;
It works great for me.
3 What's the default behaviour out of the box?
For Volley:
it will create a "volley" default cache directory for you automatically.
/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
……
}
For OkHttp:
i can't find the default cache in the source code, and we can set the response cache like this post
http://blog.denevell.org/android-okhttp-retrofit-using-cache.html
4. What's the recommended behaviour and how to achieve it?
As this post says:
Volley takes care of requesting, loading, caching, threading, synchronization and more. It’s ready to deal with JSON, images, caching, raw text and allow some customization.
I prefer to using Volley HTTP Cache because of the ease of customization.
For example, we can have much more control on the cache like this
Android Volley + JSONObjectRequest Caching.

The graceful way for OkHttp to ignore caches is:
request.setCacheControl(CacheControl.FORCE_NETWORK);

Related

Serve cache on timeout in okhttp3

In okhttp3, if my connection times out in CONNECT or READ, is there some way I can get the cache from okhttp? Instead of the connection failing, I want to serve the user from the offline cache in case the request is taking too long.
I did experience a similar issue. I wanted to fallback to cache whenever my request was timing out (I don't mind in which state) or when connection is disrupted, or when there is no connection available. To do this I made an interceptor that would first check for connectivity and after that also catch exceptions when making the request. If there is a timeout then it will throw an exception, after which we fallback to an aggressive caching.
So basically, you first need to set up your okhttp client to use cache and then use an interceptor to use that cache in a better way.
public OkHttpClient getOkHttpClient() {
File cacheFile = new File(context.getCacheDir(), "okHttpCache");
Cache cache = new Cache(cacheFile, CACHE_SIZE);
ConnectivityInterceptor connectivityInterceptor = new ConnectivityInterceptor(networkStateHelper);
OkHttpClient.Builder builder = new OkHttpClient.Builder().cache(cache).addInterceptor(connectivityInterceptor);
return builder.build();
}
After that you can use this simple interceptor to force the usage of the cache. Normally the cache is used when the server responds with 340 which means there are no changes so we can take responses that are cached, but this of course needs an active internet connection. We can however force the cache usage so it will directly take any respond from the cache if possible, which comes in handy when you are offline or when you have timeouts
public class ConnectivityInterceptor implements Interceptor {
// NetworkStateHelper is some class we have that checks if we are online or not.
private final NetworkStateHelper networkStateHelper;
public ConnectivityInterceptor(NetworkStateHelper networkStateHelper) {
this.networkStateHelper = networkStateHelper;
}
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
// You can omit this online check or use your own helper class
if (networkStateHelper.isNotOnline()) {
return getResponseFromCache(chain, request);
}
try {
Response response = chain.proceed(request);
return new Pair<>(request, response);
}
catch (Exception exception) {
Log.w(exception, "Network failure discovered, trying cache fallback");
return getResponseFromCache(chain, request);
}
}
private Response getResponseFromCache(Interceptor.Chain chain,
Request request) throws IOException {
// We just create a new request out of the old one and set cache headers to it with the cache control.
// The CacheControl.FORCE_CACHE is already provided by OkHttp3
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
// Now we proceed with the request and OkHttp should automatically fetch the response from cache or return
// a failure if it is not there, some 5xx status code
return chain.proceed(request);
}
}

OkHttp MockWebServer with dynamic URLs using Retrofit

My app uses dynamic URLs to make web-service calls (on Android). baseUrl is set as empty and we pass Retrofit2 #Url parameters in the service interface:
public interface UserService {
#GET
public Call<ResponseBody> profilePicture(#Url String url);
}
We don't know the host/domain in advance, so MockWebServer is not able to intercept the requests. The call to fetch the initial list of dynamic URLs is made in different screens. One idea is to create a new flavor providing a local data source for URLs to be used, which is my fallback plan.
I am curious if MockWebServer has any other methods to help test such cases and can be limited to test code.
You could use an OkHttp interceptor to rewrite the hostname and port?
I was also facing the same kind of issue. When I use MockWebserver in testing I have to change base URL to target mock web server localhost and port. I tried this it is working fine.
private static final Interceptor mRequestInterceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
final InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
HttpUrl httpUrl = request.url().newBuilder().scheme("http://").host(address.getHostName()).port(8080)
.build();
request = request.newBuilder()
.url(httpUrl)
.build();
return chain.proceed(request);
}
};
After this base url changes to "http://localhost:8080/"

How to make local test with okhttp

I am learning okhttp and I want to make a test with local json file in my computer or android device. But I don't know how to access local file as url string to call the function.
Like this:
File sdcard = Environment.getExternalStorageDirectory();
File testJson = new File(sdcard, "test.json");
HttpUtils.HttpGet(testJson., mCallback);
public class HttpUtils {
private static final String TAG = "HttpUtils";
private static final OkHttpClient mClient = new OkHttpClient();
public static void HttpGet(String url, Callback callback) {
//创建一个Request
final Request request = new Request.Builder()
.url(url)
.build();
//创建一个Call
Call call = mClient.newCall(request);
//请求加入调度
call.enqueue(callback);
}
}
You can use MockWebServer to serve content you load from a file.
https://github.com/square/okhttp/tree/master/mockwebserver
MockWebServer server = new MockWebServer();
// Schedule some responses.
server.enqueue(new MockResponse().setBody("hello, world!"));
server.enqueue(new MockResponse().setBody("sup, bra?"));
server.enqueue(new MockResponse().setBody("yo dog"));
// Start the server.
server.start();
// Ask the server for its URL. You'll need this to make HTTP requests.
HttpUrl baseUrl = server.url("/v1/chat/");
Well, you have to abstract your http client by some interface and create two implementation - one using OkHTTP and another - simply reading file.

Android : Volley 1.0.18 how to disable request cache?

I'm using volley library:
compile 'com.mcxiaoke.volley:library:1.0.18'
In http helper class i have the following method.
public static JsonRequest createRequest(String responseType, int requestMethod, String scheme,
String url, final String requestParams, final HttpResponseListener listener,
Request.Priority priority) throws UnsupportedEncodingException {
// Start to prepare request URL
Uri.Builder builder = new Uri.Builder();
builder.scheme(scheme).encodedAuthority(url);
// GET REQUESTS - append the params into URL
if (requestMethod == Request.Method.GET && requestParams != null) {
boolean append = appendParamsToUrl(requestParams, builder);
if(!append) return null;
}
url = URLDecoder.decode(builder.build().toString(), Constants.Request.DEFAULT_ENCODING);
// Get response as JSON object
JsonRequest request;
if (responseType.equals(Constants.Request.JSON_OBJECT)) {
// Prepare request and set the Callbacks
request = new CustomJsonObjectRequest(requestMethod, url, requestParams,
priority, responseListener(listener), errorListener(listener), listener);
}else { // Get response as JSON array of objects
// Prepare request and set the Callbacks
request = new CustomJsonArrayRequest(requestMethod, url, requestParams,
priority, responseArrayListener(listener), errorListener(listener), listener);
}
request.setTag(REQUEST_TAG);
request.setShouldCache(false);
return request;
}
When i using the option:
request.setShouldCache(false);
To force disabling cache.
But when i get the response from server from the POSTMAN (Chrome extension for API testing) i got different values in response than on the Android device.
I tried also use the:
queue.getCache().clear();
But with the same results.
How can i force disable the cache from response?
Many thanks for any advice.
request.setShouldCache(false);
does not seem to be enough for GET requests.
however, clearing the cache before adding to the queue seems to help
myRequestQueue.getCache().clear();
I put this in my getRequestQueue() method in my Volley singleton before returning requestQueue.
Call the following:
myRequestQueue.getCache().remove(url);
To use Volley without response caching, instead of using Volley.newRequestQueue(), you can create your own RequestQueue as follows:
HttpStack stack;
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
Network network = new BasicNetwork(stack);
queue = new RequestQueue(new NoCache(), network);
The key is the NoCache object which implements the Volley Cache interface but does nothing.
Bonus: If you want, you can also implement the HttpStack using OkHttp (shipped with the app). The good thing about that approach is that since you ship the OkHttp library with your app, you can rest assured that your HttpStack implementation will always work on all Android versions since you're not dependent on the platform's HttpStack implementation. Plus OkHttp has all sorts of goodies like the interceptor mechanism and a very simple API.

How to use disk cache to cache ParseFile

I'm using Parse and Picasso to load images onto ParseImageViews. Is there anything I'm missing to cache the parse files? My listview seems to be fetching the file from server every time and using the disk cache that comes with Picasso.
I don't see cache-control: max-age parameter in the http responses of downloads of parse files(from amazon s3 where parse stores them)
I have the following code,
final ParseImageView pic = viewHolder.img;
pic.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
ParseFile f = parseObject.getParseFile("image");
Picasso.with(mContext).load(f.getUrl()).into(pic);
Any help would be appreciated. Thanks.
Use OkHttp client as http transport for Picasso and specify disk and memory cache size:
OkHttpClient okHttp = new OkHttpClient();
Cache cache = new Cache(ctx.getCacheDir(), cacheSize);
okHttp.setCache(cache);
// Use OkHttp as downloader
Downloader downloader = new OkHttpDownloader(okHttp);
mPicasso = new Picasso.Builder(getApplicationContext())
.downloader(downloader)).memoryCache(new LruCache(size)).build();
Setup request interceptor (example) for OkHttp client:
// Add Cache-Control to origin response (force cache)
client.networkInterceptors().add(new Interceptor() {
private com.squareup.okhttp.Request request;
private Response response;
private String requestUrl;
#Override
public Response intercept(Chain c) throws IOException {
request = c.request();
response = c.proceed(request);
if (!request.cacheControl().noStore()
&& !response.cacheControl().noStore()) {
requestUrl = request.urlString();
// Do not cache keys or playlists
response = response
.newBuilder()
.header("Cache-Control","public, max-age=42000").build();
}
return response;
}
});

Categories

Resources