jademcosta
jademcosta

Reputation: 1528

FileNotFoundException when using the offline cache of HttpResponsecache

I'm using HttpResponseCache to enable the response caching (for web requests) in my android app, and the offline cache isn't working. I'm doing the offline cache as the documentation tells me to do.

In my Application class, at the onCreate method, I'm turning on the the cache with:

try {
    long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
    File httpCacheDir = new File(getCacheDir(), "http");
    Class.forName("android.net.http.HttpResponseCache")
        .getMethod("install", File.class, long.class)
        .invoke(null, httpCacheDir, httpCacheSize);
} catch (Exception httpResponseCacheNotAvailable) {}

At my HttpConnection class I'm getting the JSON with the method:

private String sendHttpGet(boolean cacheOnly) throws Exception {

    URL url = new URL(getUrlCompleta());
    HttpURLConnection urlConnection = null;
    String retorno = null;

    try {
        urlConnection = (HttpURLConnection) url.openConnection();
        if(urlConnection == null)
            throw new Exception("Conn obj is null");

        fillHeaders(urlConnection, cacheOnly);
        InputStream in = new BufferedInputStream(urlConnection.getInputStream(), 8192);
        retorno = convertStream(in);
        in.close();
        urlConnection.disconnect();

        if(retorno != null)
            return retorno;
    } catch(IOException e) {
        throw e;
    } finally {
        if(urlConnection != null)
            urlConnection.disconnect();
    }
    throw new Exception();
}

Where the convertStream method just parse a InputStream into a String. The method fillHeaders put an token on the request (for security reasons) and if the parameter cacheOnly is true, then the header "Cache-Control", "only-if-cached" is added to the request header ( with the code: connection.addRequestProperty("Cache-Control", "only-if-cached");)

The cache works 'fine' (with minor strange behaviors) when there is connectivity and the app hit the web server just to see if there is a newer version of the JSON. When the web server answers "nothing changed", the cache works.

The problem is when I have no connectivity and use the header "Cache-Control", "only-if-cached". In this case, I receive a java.io.FileNotFoundException: https://api.example.com/movies.json. That is awkward, because the implementation code of the cache probably stores the response in a file named using a hash function on the request url, and not the url itself.

Does anyone knows what can I do or what is wrong with my implementation?

ps: Above, I said "probably using a hash function", because I was not able to found the implementation of the com.android.okhttp.HttpResponseCache object (the class that android.net.http.HttpResponseCache delegates cache calls). If someone found it, please tell me where to look at :)

ps2: Even when I add a max-stale parameter in the Cache-Control header, it still doesn't work.

ps3: I obviously tested it on api 14+.

ps4: Although I'm accessing an "https://" URL address, the same behavior occurs when the URL is just a normal "http://" address.

Upvotes: 1

Views: 959

Answers (1)

jademcosta
jademcosta

Reputation: 1528

It turns out that the problem was with the max-age value of the Cache-control directive in the response given by my web server. It had the following value: Cache-Control: max-age=0, private, must-revalidate. With this directive, my server was saying to the cache that the response could be used from the cache even if it was 0 seconds old. So, my connection wasn't using any cached response.

Knowing that max-age is specified in seconds, all I had to do was change the value to: Cache-Control: max-age=600, private, must-revalidate! There it is, now I have a 10 minute cache.

Edit: If you want to use a stale response, with the max-stale directive of the request, you shouldn't use the must-revalidate directive in the response, as I did in my webserver.

Upvotes: 3

Related Questions