Glide: Cache-misses when playing a paused GIF for the first time because of transcoder differences in cache key

Created on 1 Feb 2017  路  10Comments  路  Source: bumptech/glide

Glide Version: com.github.bumptech.glide:glide:3.7.0

Integration libraries: com.github.bumptech.glide:okhttp3-integration:1.4.0

Issue details / Repro steps / Use case background: I'm following suggestion similar to https://github.com/bumptech/glide/issues/1065#issuecomment-197886330 to load a "paused" GIF and then playing the GIF on demand. When I do this the hosting ImageView removes the first frame and it's shown empty, while a new network requests is happening in the background for the same .gif resource. Only after the (redundant) 2nd request completes the GIF starts playing in the ImageView.

Glide load line / GlideModule (if any) / list Adapter code (if any):

// load the "paused" GIF
Glide.with(hostActivity).load(url).asBitmap().into(imageView);

// "play" the "already-cached" GIF
 Glide.with(hostActivity).load(url).into(imageView);


Stack trace / LogCat:

Engine: Started new load in 0.106072ms, key: EngineKey{https://www.host.com/image.gif+com.bumptech.glide.signature.EmptySignature@6a502a0+[865x572]+''+'ImageVideoBitmapDecoder.com.bumptech.glide.load.resource.bitmap'+'CenterCrop.com.bumptech.glide.load.resource.bitmap'+'BitmapEncoder.com.bumptech.glide.load.resource.bitmap'+''+''}

AuthTokenOkHttpInterceptor: Adding Authorization header to request with url=https://www.host.com/image.gif

GlideImageLoader: onResourceReady(resource:com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable@7e0e310, model:https://www.host.com/image.gif, target:Target for: android.support.v7.widget.AppCompatImageView{e4fbe09 VFED..C.. ........ 3,3-868,575 #7f100162 app:id/imageThumbnail}, isFromMemoryCache:false, isFirstResource:true)

[...]

Engine: Started new load in 0.054615ms, key: EngineKey{https://www.host.com/image.gif+com.bumptech.glide.signature.EmptySignature@6a502a0+[865x572]+''+'ImageVideoBitmapDecoder.com.bumptech.glide.load.resource.bitmap'+'CenterCrop.com.bumptech.glide.load.resource.bitmap'+'BitmapEncoder.com.bumptech.glide.load.resource.bitmap'+'GifBitmapWrapperDrawableTranscoder.com.bumptech.glide.load.resource.transcode'+''}

AuthTokenOkHttpInterceptor: Adding Authorization header to request with url=https://www.host.com/image.gif

GlideImageLoader: onResourceReady(resource:com.bumptech.glide.load.resource.gif.GifDrawable@2050ba3, model:https://www.host.com/image.gif, target:Target for: android.support.v7.widget.AppCompatImageView{e4fbe09 VFED..C.. ........ 3,3-868,575 #7f100162 app:id/imageThumbnail}, isFromMemoryCache:false, isFirstResource:true)

As you can see above there is a difference between the 2 cache keys, since the "paused" request is missing the GifBitmapWrapperDrawableTranscoder, what's the right way to make the keys to match?

question

All 10 comments

.asBitmap() returns a Bitmap, while normal load returns a magic drawable that decodes the frames on the fly. You can't have the two have the same cache key.

What you can do is cache the original image by adding .diskCacheStrategy(SOURCE) to both loads. This will share the downloaded image between the two loads and not re-download it. Additionally if you use .diskCacheStrategy(ALL) on the .asBitmap() load you'll get the resized "paused" image cached for faster load (useful in scrolling containers). Notice that it's only for the .asBitmap() load, the GIF loads can be extremely slow if the RESULT cache is in play.

1065 is the solution you are looking for, but you can't simplify it more than: removing.error(), .placeholder() and .override(). All the other stuff plays a key role in making Glide behave the way you want. Read my explanation and comments carefully.

Thanks a lot for your reply, I'll give it a try.

Another semi-related question @TWiStErRob, would the .disCacheStrategy() behavior you described be affected in any way if we have opted-out from using Glide's disk cache (by adding this configuration to our custom GlideModule):

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        // disabling Glide internal cache since we'll be using OkHttp's cache
        builder.setDiskCache(new DiskCache.Factory() {
            @Override
            public DiskCache build() {
                // this is a no-op cache: https://github.com/bumptech/glide/wiki/Configuration#disk-cache
                return new DiskCacheAdapter();
            }
        });
    }

and are using OkHttpClient's cache instead?

You'll always get a cache miss with the adapter, so .diskCacheStrategy has no effect at all, all 4 values behave the same and query an OkHttp stream, which will be served either from disk or from network.

I'm not entirely sure how GIF loading speed would be affected, my hunch is that it'll be as fast as SOURCE regardless of diskCacheStrategy, because DiskCache.put arguments are ignored.

I see, so what would be your suggestion for hitting the disk cache for the GIF pause/play scenario using OkHttpClient's cache? (since .diskCacheStrategy() won't work) Am I out of luck?

What I said is that OkHttp and SOURCE should be on par performance-wise for GIFs; sorry if it wasn't clear.

Hmm, so it should work then?

Maybe I didn't explain myself very well before, in my original issue report I was already using the OkHttpClient's cache, and I'm still seeing the behavior I reported (double network requests), I'm assuming because the cache key is different.

Is there any way I can circumvent this behavior and use the disk-cached image without resorting to the . diskCacheStrategy() option?

Glide requests OkHttp if there's no cache, and it sends the https://www.host.com/image.gif to it. The question is that your OkHttp cache is working? So there'll be always a network request, but OkHttp may not actually go onto the wire to serve it. Check their logs of what's actually happening.

More details about the issue here: https://github.com/square/okhttp/issues/3152

Seems to have more to do with the way OkHttp caches and the way BitmapFactory decodes the stream.

Although the suggestion is to provide some way to force the remaining consumption of the stream inside Glide after calling BitmapFactory.decodeStream()

Would that make sense?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

billy2271 picture billy2271  路  3Comments

kenneth2008 picture kenneth2008  路  3Comments

technoir42 picture technoir42  路  3Comments

PatrickMA picture PatrickMA  路  3Comments

mttmllns picture mttmllns  路  3Comments