Skip to content

OkHttp offline caching issue #1200

@ChrisMpitzios

Description

@ChrisMpitzios

Hello! I am developing an Android app for which i use Retrofit and OkHttp and i am facing a problem regarding offline caching (the device that currently i am testing on is an HTC One m8 android version 4.3 but other devices that i used faced the same problem) .
I am using one RestAdapter which is being initialized once. During initialization i am passing to it a retrofit OkClient initialized with an OkHttp client ->
private RestAdapter getRestAdapterInstance() {
if (sRestAdapterInstance == null) {
OkHttpClient httpClient = new OkHttpClient();
Cache responseCache = null;
try {
File cacheDir = new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
responseCache = new Cache(cacheDir/mApplicationContext.getCacheDir()/, HTTP_CLIENT_CACHE_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
if (responseCache != null) {
httpClient.setCache(responseCache);
}
sRestAdapterInstance = new RestAdapter.Builder().setEndpoint(sBaseUrl)
.setRequestInterceptor(this)
.setClient(new OkClient(httpClient))
.build();
sRestAdapterInstance.setLogLevel(LogLevel.FULL);
}
return sRestAdapterInstance;
}

The problem is that during offline mode the responses are considered immediately stale despite the fact that the server contains max-age: 300 (so 5 minutes..).
Here is one response from the server with loglevel full ->

//request online//
12-14 23:01:05.921 D/engine.readResponse()﹕ engine.readResponse()
12-14 23:01:06.001 D/CategoriesRootActivity﹕ OnStop
12-14 23:01:06.261 D/Retrofit﹕ ---> HTTP GET https://api.skroutz.gr/categories/1269/children?page=1
12-14 23:01:06.261 D/Retrofit﹕ Accept: application/vnd.skroutz+json;version=3
12-14 23:01:06.261 D/Retrofit﹕ Authorization: Bearer ta3XXPWPPdpxxxxxxxxxx
12-14 23:01:06.261 D/Retrofit﹕ User-Agent: skroutz.android.app
12-14 23:01:06.261 D/Retrofit﹕ ---> END HTTP (no body)
12-14 23:01:06.261 ﹕ VFY: unable to resolve virtual method 44655: Lcom/squareup/okhttp/internal/huc/HttpURLConnectionImpl;.getContentLengthLong ()J
12-14 23:01:06.261 ﹕ VFY: unable to resolve virtual method 44660: Lcom/squareup/okhttp/internal/huc/HttpURLConnectionImpl;.getHeaderFieldLong (Ljava/lang/String;J)J
12-14 23:01:06.732 D/Retrofit﹕ <--- HTTP 200 https://api.skroutz.gr/categories/1269/children?page=1 (467ms)
12-14 23:01:06.732 D/Retrofit﹕ : HTTP/1.1 200 OK
12-14 23:01:06.732 D/Retrofit﹕ Accept-Ranges: bytes
12-14 23:01:06.732 D/Retrofit﹕ Access-Control-Allow-Methods: HEAD, GET, POST, PUT, DELETE
12-14 23:01:06.732 D/Retrofit﹕ Access-Control-Allow-Origin: *
12-14 23:01:06.742 D/Retrofit﹕ Age: 0
12-14 23:01:06.742 D/Retrofit﹕ Cache-Control: max-age=300, private
12-14 23:01:06.742 D/Retrofit﹕ Content-Type: application/json; charset=utf-8
12-14 23:01:06.742 D/Retrofit﹕ Date: Sun, 14 Dec 2014 21:03:03 GMT
12-14 23:01:06.742 D/Retrofit﹕ OkHttp-Received-Millis: 1418594466675
12-14 23:01:06.742 D/Retrofit﹕ OkHttp-Response-Source: NETWORK 200
12-14 23:01:06.742 D/Retrofit﹕ OkHttp-Selected-Protocol: http/1.1
12-14 23:01:06.752 D/Retrofit﹕ OkHttp-Sent-Millis: 1418594466402
12-14 23:01:06.752 D/Retrofit﹕ Status: 200 OK
12-14 23:01:06.752 D/Retrofit﹕ Vary: Accept-Encoding
12-14 23:01:06.752 D/Retrofit﹕ X-Cache: MISS
12-14 23:01:06.752 D/Retrofit﹕ X-DNS-Prefetch-Control: off
12-14 23:01:06.752 D/Retrofit﹕ X-RateLimit-Limit: 100
12-14 23:01:06.752 D/Retrofit﹕ X-RateLimit-Remaining: 99
12-14 23:01:06.752 D/Retrofit﹕ X-RateLimit-Reset: 1418591040
12-14 23:01:06.752 D/Retrofit﹕ X-Request-Id: 1855210501
12-14 23:01:06.752 D/Retrofit﹕ X-UA-Compatible: IE=Edge,chrome=1
12-14 23:01:06.762 D/Retrofit﹕ {"categories":[{"id":22,"name":"\u0397........le":"\u039a\u03b1\u03c4\u03b1\u03c3\u03ba\u03b5\u03c5\u03b1\u03c3\u03c4\u03ad\u03c2"}],"meta":{"pagination":{"total_results":8,"total_pages":1,"page":1,"per":25}}}
12-14 23:01:06.762 D/Retrofit﹕ <--- END HTTP (2775-byte body)

//request offline

12-14 23:05:08.760 z D/Retrofit﹕ ---> HTTP GET https://api.skroutz.gr/categories/1269/children?page=1
12-14 23:05:08.760 D/Retrofit﹕ Accept: application/vnd.skroutz+json;version=3
12-14 23:05:08.760 D/Retrofit﹕ Authorization: Bearer ta3XXPWPPdp2xxxxxxxxx
12-14 23:05:08.760 D/Retrofit﹕ User-Agent: skroutz.android.app
12-14 23:05:08.770 D/Retrofit﹕ ---> END HTTP (no body)
12-14 23:05:08.790 D/Retrofit﹕ ---- ERROR https://api.skroutz.gr/categories/1269/children?page=1
12-14 23:05:08.800 D/Retrofit﹕ java.net.UnknownHostException: Unable to resolve host "api.skroutz.gr": No address associated with hostname
at java.net.InetAddress.lookupHostByName(InetAddress.java:434)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:239)
at java.net.InetAddress.getAllByName(InetAddress.java:214)
at com.squareup.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:260)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:234)
at com.squareup.okhttp.internal.http.RouteSelector.nextUnconnected(RouteSelector.java:159)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:133)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:332)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:268)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:420)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:371)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:466)
at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:73)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:38)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
at retrofit.RestAdapter$RestHandler$2.obtainResponse(RestAdapter.java:278)
at retrofit.CallbackRunnable.run(CallbackRunnable.java:42)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at retrofit.Platform$Android$2$1.run(Platform.java:142)
at java.lang.Thread.run(Thread.java:864)
Caused by: libcore.io.GaiException: getaddrinfo failed: EAI_NODATA (No address associated with hostname)
at libcore.io.Posix.getaddrinfo(Native Method)
at libcore.io.ForwardingOs.getaddrinfo(ForwardingOs.java:61)
at java.net.InetAddress.lookupHostByName(InetAddress.java:415)
            at java.net.InetAddress.getAllByNameImpl(InetAddress.java:239)
            at java.net.InetAddress.getAllByName(InetAddress.java:214)
            at com.squareup.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
            at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:260)
            at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:234)
            at com.squareup.okhttp.internal.http.RouteSelector.nextUnconnected(RouteSelector.java:159)
            at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:133)
            at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:332)
            at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:268)
            at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:420)
            at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:371)
            at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:466)
            at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
            at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
            at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:73)
            at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:38)
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
            at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
            at retrofit.RestAdapter$RestHandler$2.obtainResponse(RestAdapter.java:278)
            at retrofit.CallbackRunnable.run(CallbackRunnable.java:42)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
            at retrofit.Platform$Android$2$1.run(Platform.java:142)
            at java.lang.Thread.run(Thread.java:864)
12-14 23:05:08.800 6688-7222/com.niobiumlabs.android.apps.skroutz D/Retrofit﹕ ---- END ERROR

//response offline after setting --> request.addHeader("Cache-Control", "max-stale=3200" );

12-14 23:21:49.638 D/Retrofit﹕ ---> HTTP GET https://api.skroutz.gr/categories/1269/children?page=1
12-14 23:21:49.638 D/Retrofit﹕ Accept: application/vnd.skroutz+json;version=3
12-14 23:21:49.638 D/Retrofit﹕ Authorization: Bearer MWBft13PUYBE/zxxxxxxxxxxxxxx
12-14 23:21:49.638 D/Retrofit﹕ User-Agent: skroutz.android.app
12-14 23:21:49.638 D/Retrofit﹕ Cache-Control: max-stale=3200
12-14 23:21:49.638 D/Retrofit﹕ ---> END HTTP (no body)
12-14 23:21:49.648 D/Retrofit﹕ <--- HTTP 200 https://api.skroutz.gr/categories/1269/children?page=1 (10ms)
12-14 23:21:49.648 D/Retrofit﹕ : HTTP/1.1 200 OK
12-14 23:21:49.648 D/Retrofit﹕ Accept-Ranges: bytes
12-14 23:21:49.648 D/Retrofit﹕ Access-Control-Allow-Methods: HEAD, GET, POST, PUT, DELETE
12-14 23:21:49.648 D/Retrofit﹕ Access-Control-Allow-Origin: *
12-14 23:21:49.648 D/Retrofit﹕ Age: 0
12-14 23:21:49.648 D/Retrofit﹕ Cache-Control: max-age=300, private
12-14 23:21:49.648 D/Retrofit﹕ Content-Type: application/json; charset=utf-8
12-14 23:21:49.648 D/Retrofit﹕ Date: Sun, 14 Dec 2014 21:23:36 GMT
12-14 23:21:49.648 D/Retrofit﹕ OkHttp-Received-Millis: 1418595700171
12-14 23:21:49.648 D/Retrofit﹕ OkHttp-Response-Source: CACHE 200
12-14 23:21:49.648 D/Retrofit﹕ OkHttp-Selected-Protocol: http/1.1
12-14 23:21:49.648 D/Retrofit﹕ OkHttp-Sent-Millis: 1418595699893
12-14 23:21:49.648 D/Retrofit﹕ Status: 200 OK
12-14 23:21:49.648 D/Retrofit﹕ Vary: Accept-Encoding
12-14 23:21:49.648 D/Retrofit﹕ Warning: 110 HttpURLConnection "Response is stale"
12-14 23:21:49.648 D/Retrofit﹕ X-Cache: MISS
12-14 23:21:49.648 D/Retrofit﹕ X-DNS-Prefetch-Control: off
12-14 23:21:49.658 D/Retrofit﹕ X-RateLimit-Limit: 100
12-14 23:21:49.658 D/Retrofit﹕ X-RateLimit-Remaining: 99
12-14 23:21:49.658 D/Retrofit﹕ X-RateLimit-Reset: 1418592240
12-14 23:21:49.658 D/Retrofit﹕ X-Request-Id: 1855403901
12-14 23:21:49.658 D/Retrofit﹕ X-UA-Compatible: IE=Edge,chrome=1
12-14 23:21:49.658 D/Retrofit﹕ {"categories":[{"id":22,"name":"\u0397\u....
12-14 23:21:49.658 D/Retrofit﹕ <--- END HTTP (2775-byte body)

The only modification i have made is this in CacheControl class:

else if ("public".equalsIgnoreCase(directive)) {
isPublic = true;
} else if ("private".equalsIgnoreCase(directive)) {
isPublic = true;
} else if ("must-revalidate".equalsIgnoreCase(directive)) {
mustRevalidate = true;
}
in order to avoid the private indication that the server's response Cache header contains (from what i figured out , OkHttp needs public in response's Cache header).

The message i get is "unable to resolve host" but there is nothing in server's headers that indicate that the response must be revalidated or something (except if i am missing something).
Thing i tried and didnt work are:
1)Changing OkHttp's client cache size from 1 Mb to 10Mb ->
private static final int HTTP_CLIENT_CACHE_SIZE = 10 * 1024 * 1024;
//private static final int HTTP_CLIENT_CACHE_SIZE = 1024 * 1024;
2)Changing OkHttp's client cache as shown from the first snippet (in case there was a problem with one of these two)
3)I am using picasso so i removed its usage from the activity that makes the call in case there is a conflict when these two work together.
4)tried to set cache header to "public " using interceptor ->
@OverRide
public void intercept(RequestFacade request) {
request.addHeader(HTTP_HEADER_ACCEPT_HEADER, mAcceptHeader);//application/vnd.skroutz+json
request.addHeader(HTTP_HEADER_AUTHORIZATION, HTTP_HEADER_AUTHORIZATION_VALUE_PREFIX + mSession.getAccessToken());
request.addHeader(HTTP_HEADER_USER_AGENT, mUserAgent);
if (SKUtilities.isInternetAvailable(mApplicationContext)) {
} else {
request.addHeader("Cache-Control", "public");
}
}
5)Tried to set cache header to "public" dynamically in retrofit by passing it during the call (in case there was a conflict layer-wise) ->
@get("/categories/{id}/children")
void getCategoryChildren(@Header("Cache-Control") String cacheHeaders,@path("id") int id, @QueryMap Map<String, Object> args, Callback cb);

Nothing finally worked. The only thing that did work was setting cache header (client side) to "max-stale:3200" ->
request.addHeader("Cache-Control", "max-stale=3200");
This is a threshold that strangely worked (below 3200 didnt work and i dont know why, even when setting 3200 after a 2-3 minutes minutes the response is considered stale again)
In the general case (without setting cache header) the response is considered stale immediately after the call.

This is really urgent and it would be highly appreciated if you could respond soon.
Thank you in advance!
Chris

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions