xunice / libs-for-android

Automatically exported from code.google.com/p/libs-for-android
Apache License 2.0
0 stars 0 forks source link

Using file-cache and image-loader together cannot cache image correctly (workaround attached) #17

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?

1. Use file-cache and image-loader together for image cache

new ImageLoader(HttpResponseCache.capture(new BitmapContentHandler(), null), 
HttpResponseCache.capture(HttpResponseCache.sink(), null));

public class HttpResponseCache extends FileResponseCache {
    @Override protected File getFile(final URI uri, final String requestMethod, final Map<String, List<String>> requestHeaders, final Object cookie) {
        ...
    }
    public static void install(final Context context) {
        ...
    }
}

2. Cache an image when online
3. Go offline and the cached image could not be loaded.

What is the expected output? What do you see instead?

The cached image should be loaded as expected for file-cache, but there's an 
inconsistent behavior between file-cache and HttpEngine that caused HttpEngine 
incorrectly identified the cache as unavailable.

What version of the product are you using? On what operating system?

Android 4.0.4.

[Workaround]

Explicitly add an null entry in response header to fulfill the expectation of 
HttpEngine about status line:

public class HttpResponseCache extends FileResponseCache {
    ...
    @Override public CacheResponse get(final URI uri, final String requestMethod, final Map<String, List<String>> requestHeaders) throws IOException {
        final CacheResponse response = super.get(uri, requestMethod, requestHeaders);
        if (response == null) return null;

        /* Workaround for HttpEgnine to correctly identify cache source */
        final Map<String, List<String>> headers = response.getHeaders();
        final List<String> status = headers.get("status");
        status.set(0, "HTTP/1.0 " + status.get(0));
        headers.put(null, status);

        return response;
    }
}

Original issue reported on code.google.com by oasisfeng on 4 Jun 2012 at 10:11

GoogleCodeExporter commented 9 years ago
I have the same problem on Android 4.0.4. I have tried the Workaround above 
(thanks for the post) but it didn't change the fact that the image is 
redownloaded each time I need it.
I have logged the process and here is what I can see: I try to display the 
already downloaded image, the [FileResponseCache].get(...) is called (I've 
checked and getFile(...) returns the correct filename) and returns a 
CacheResponse using createCacheResponse(file), but right after that the 
[FileResponseCache].put() is called, resulting to the re-download of the image.
Any idea ? Thanks for your help

Original comment by franr...@gmail.com on 8 Jun 2012 at 1:42

GoogleCodeExporter commented 9 years ago
I have been using a back port component of HttpResponseCache in Android 3.0+ 
from https://github.com/candrews/HttpResponseCache

It is actually working well together with image-loader.

Original comment by oasisfeng on 24 Jun 2012 at 1:46

GoogleCodeExporter commented 9 years ago
I've found in order to get this to work I need to set some other headers as 
well or the response is incorrectly identified as stale. The following code 
should fix the problem (it goes in your class which inherits from 
FileResponseCache)

    @Override
    public CacheResponse get(URI uri, String requestMethod, Map<String, List<String>> requestHeaders) throws IOException {
        final CacheResponse response = super.get(uri, requestMethod, requestHeaders);
        if (response == null) return null;

        /* Workaround for HttpEngine to correctly identify cache source */
        final Map<String, List<String>> headers = response.getHeaders();

        addHeader(headers, null, "HTTP/1.1 200 OK");
        addHeader(headers, "cache-control", "max-age=99999");
        String now = String.valueOf(new Date().getTime());
        addHeader(headers, "X-Android-Received-Millis", now);
        addHeader(headers, "X-Android-Sent-Millis", now);

        return response;
    }

Original comment by rupert.b...@guardian.co.uk on 11 Oct 2012 at 11:04

GoogleCodeExporter commented 9 years ago
First, thanks a lot for the solutuion!

I noticed similarly that the cache wasn't working correctly on newer Androids, 
and it was still going at the network - visible when passing the traffic 
through Fiddler (Windows) or Squid (Unix, tail -f /var/log/squid3/access.log).

Debugging through the sources after reading this thread I found that the in 
HttpEngine.initResponseSource() the check was always coming back with 
ResponseSource.NETWORK, and was mainly because the headers didn't contain the 
"null" status code entry for RawHeaders.fromMultimap() to call in 
setStatusLine() and set a valid responseCode (-1 otherwise).

I finally made the change in my clone (dandar33-libs-for-android) in 
FileCacheResponse.getHeaders() as it seemed a better place for it and I need it 
in a couple of other projects - I tested it on Android 3.0 and earlier and the 
fix doesn't seem to be needed. In fact, it will throw an NPE in 2.3 as it holds 
the headers in a SortedMap. So I made the fix for 200 OK for API 14 and later.

Thanks again for the good work, this annoyed me for quite a while! 

References:

http://source.android.com/source/build-numbers.html
https://android.googlesource.com/platform/libcore/+/android-4.0.4_r1/luni/src/ma
in/java/libcore/net/http/HttpEngine.java
https://android.googlesource.com/platform/libcore/+/android-4.0.4_r1/luni/src/ma
in/java/libcore/net/http/RawHeaders.java

Original comment by dan.dar33@gmail.com on 9 Jun 2013 at 9:54