question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Disk caching doesn't work when using BitmapFactory.decodeStream() with GIFs

See original GitHub issue

It looks like when using OkHttp to hit an url that will return a GIF file, and then consuming the returned stream with BitmapFactory.decodeStream() the file won’t be cached into disk.

If I understand correctly the way okhttp3.internal.cache.CacheInterceptor works, it will use a okio.ForwardingSink to communicate the stream interactions to the okhttp3.Cache class, in order to signal its DiskLruCache.Editor to commit() to disk the stream that has just been exhausted and closed.

The problem seems to arise when consuming a GIF stream with BitmapFactory.decodeStream() which will load a single frame and very likely doesn’t bother reading more than header+first frame from the stream, thus never triggering okhttp cache to write all the bytes to disk.

Is there any way to work around this or signal OkHttp to forcefully write its stream to disk cache?

I worked around this issue by writing this hacky Interceptor:

/**
 * This interceptor is helpful if you want to force OkHttp to write the response to disk cache
 * immediately after receiving it (instead of waiting for the response body stream to be consumed).
 * Ideally it should only be used as a network interceptor (since application interceptors also
 * intercept when fetching from cache)
 */
public class DiskCacheEnforcerOkHttpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        Logger.verbose("Flushing OkHttp Response bytes to force disk caching.");
        // by calling response.body().bytes() we force the stream to be consumed and thus cached to disk
        ResponseBody newResponseBody = ResponseBody.create(response.body().contentType(), response.body().bytes());
        return response.newBuilder().body(newResponseBody).build();
    }
}

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:1
  • Comments:8 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
swankjessecommented, Mar 18, 2017

Oooh, this is a tough case. I think your interceptor is actually a pretty great idea. You can make it even more efficient by using BufferedSource.request():

public class DiskCacheEnforcerOkHttpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        response.body().source().request(10 * 1024 * 1024);
        return response;
    }
}

That’ll pull the data until it’s loaded either 10 MiB or the end of the file.

Alternately you could do a ForwardingSource that calls skip() on close(). That way you only skip the unwanted bytes after you’ve downloaded the image.

0reactions
swankjessecommented, Apr 30, 2017

No action to take here.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Android image caching
When you use an HttpResponseCache , you might find the HttpResponseCache.getHitCount() returning 0. I'm not sure but I think it's because the ...
Read more >
Using Picasso? Watch out for half black images!
The reason is, that Picasso doesn't check if Bitmap converted from Stream is correct. It uses Androids bitmap decoder ( BitmapFactory.
Read more >
Sending and Managing Network Requests
Sending an HTTP Request (Third Party) ; Debugging, Use Stetho, Use LogInterceptor ; Disk Caching, Yes, Yes ; Request Queueing, No, No ;...
Read more >
IMGDroid: Detecting Image Loading Defects in Android ...
If the bitmap is not in the memory cache, the app then tries to read it from the disk cache. If it succeeds,...
Read more >
BitmapFactory.decodeStream() fails if InputStream.skip() does ...
decodeStream() method fails to read a JPEG image (i.e. ... Bitmap bmp = BitmapFactory. ... This is slightly different, you are not using...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found