Ensuring That Images Loaded Only Come From Disk Cache

10,098 views
Skip to first unread message

ble...@gmail.com

unread,
Nov 6, 2014, 11:41:47 PM11/6/14
to glidel...@googlegroups.com
Hello,

I'm writing an app that preloads a bunch of images from the Web using the DownloadOnly example (see link below) via an AsyncTask so that the images are forced into the DiskCache.  From there I'd like to only load these images from the cache (needed for offline support), but when I try the .diskCacheStrategy(DiskCacheStrategy.ALL) technique to load them again into an ImageView if it doesn't find the images in the DiskCache it then loads them from the Web again.  Ideally I'd like to find a way where Glide would tell me that it couldn't find the request in DiskCache and then stop processing.  Is this possible?


Thanks!

Brad

Róbert Papp

unread,
Nov 7, 2014, 5:32:25 AM11/7/14
to ble...@gmail.com, glidel...@googlegroups.com

Hi Brad,
Good question, we've just talked about a similar thing yesterday, check the thread before yours in Google Groups.

If it's in cache and it doesn't pick it up next time you load it, that means that the cache key is different for the two loads. EVERY parameter must match: size, decoders, transcoders, transformation, size, etc...
So if you preload with a certain size your target must have the same size. (I hope you see the emphasis on size :)

If Glide is not able to download the image (no network) and it's not in cache you should get the .error() image. That means that by adding a .listener() you should be able to find out when a cache miss happens. Check all those booleans in the method signatures of the listener interface as well.

Also you can try increasing the cache size: maybe you're preloading too much and it evicts most of them because of later ones.

Cheers
+Robi

--
You received this message because you are subscribed to the Google Groups "Glide" group.
To unsubscribe from this group and stop receiving emails from it, send an email to glidelibrary...@googlegroups.com.
To post to this group, send email to glidel...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/glidelibrary/49cca70b-63b3-42a0-98ba-9f244d23438c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

ble...@gmail.com

unread,
Nov 7, 2014, 10:29:17 AM11/7/14
to glidel...@googlegroups.com, ble...@gmail.com
Hi Robi,

Thanks so much for your insight and confirming that the previous thread was on a similar track.


I think I have it working now.  You weren't kidding about the emphasis on size!  :-)  I believe that's why direct loading using .into(ImageView) was causing problems (even though it was hardcoded to the same size), but when I replaced it with the SimpleTarget with explicit size it seemed to function as desired.  That said, I'm still new to the library so I was wondering if you'd do a quick sanity check on my logic to make sure I'm not getting a false positive.

Thanks again for your help!

Brad

// Loading From Web Into DiskCache Logic
    private class DownloadTileToCacheTask extends AsyncTask<String, Void, File> {
        @Override
        protected File doInBackground(String... params) {
            FutureTarget<File> future = Glide.with(getApplicationContext())
                    .load(params[0])
                    .downloadOnly(256, 256);

            File file = null;
            try {
                file = future.get();
            } catch (Exception e) {
                e.printStackTrace();
            }

            return file;
        }
    }

// Called by
new DownloadTileToCacheTask().execute("http://api.tiles.mapbox.com/v3/examples.map-zr0njcqy/0/0/0.png");


// Load Image From DiskCache
    private void loadImageFromDiskCache() {
        final ImageView tileView = (ImageView)findViewById(R.id.cacheTileImageView);
        // Loads image from network if not found in disk cache
//        Glide.with(this).load(tileURL).diskCacheStrategy(DiskCacheStrategy.ALL).into(tileView);
        Glide.with(this).load(tileURL).diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GlideDrawable>() {
            @Override
            public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
                Log.i(TAG, "Listener onException: " + e.toString());
                return false;
            }

            @Override
            public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                Log.i(TAG, "onResourceReady with resource = " + resource);
                Log.i(TAG, "onResourceReady from memory cache = " + isFromMemoryCache);
                return false;
            }
        })
        .into(new SimpleTarget<GlideDrawable>(256, 256) {
            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
                Log.i(TAG, "GlideDrawalble = '" + resource + "'");
                tileView.setImageDrawable(resource.getCurrent());
            }
        });
    }



Sam Judd

unread,
Nov 7, 2014, 11:18:16 AM11/7/14
to ble...@gmail.com, glidel...@googlegroups.com
Hi Brad,

Two things. First if you're loading into an ImageView and want a specific size that isn't necessarily the size of the view, I still recommend passing the View in directly. You can use the .override() API to override the View's size with your specific size.

Second, if I'm understanding you correctly, you want to only load images from the disk cache in your ui and fail if the image isn't present in the disk cache?

If so you actually need to do a little bit more. Check out the .using() API and the StreamModelLoader interface. If you want to disable downloads, you will need to implement a custom StreamModelLoader that returns a DataFetcher that does nothing (just throwing an IOException would be fine) and then pass it in to your request with the using() API. Doing so will replace Glide's default loaders and prevent any RPCs, causing the load to fail if the image can't be loaded from the disk cache.

Sam

Also as an aside, Glide has two methods for caching images. DiskCacheStrategy.SOURCE means the original bytes of whatever you request are copied directly into our cache using only the url (file path, uri etc) that you provided as the cache key. As long as the url of subsequent requests match, those requests will all hit the cache, regardless of size or options. DiskCacheStrategy.RESULT, the default, means that only the final transformed bytes of the image are written to cache. To get a cache hit, subsequent requests must request exactly the same size with the same set of decoders and transformations. DiskCacheStrategy.ALL means both the original bytes will be cached and, separately, the transformed bytes will be cached. For your use case, probably SOURCE is sufficient, although if you're displaying small thumbnails of large images, ALL may result in cached loads completing more quickly.

nem...@gmail.com

unread,
Dec 2, 2014, 7:02:58 PM12/2/14
to glidel...@googlegroups.com, ble...@gmail.com
Any chance one can achieve the same as above, but *synchronously*?

The last part is critical in my case, since I am trying to do a shared element transition between fragments in Android 5.0, and the resource must be actually set in the ImageView *during* the onCreateView call of the entering fragment. If I just pass the image URL from the exiting fragment to the entering one and set it to my target (shared element) ImageView using a normal Glide.with().load(url) call, it does not work, since it is done asynchronously and the resource is not yet actually set in the ImageView when the fragment transition (ChangeImageTransform) captures the states of the 2 ImageViews in order to perform the transition.

I can always make it work by manually saving the image to disk in the first fragment and then loading it in the second, but this feels like an overkill (with great overhead), since I am sure that the image has definitely been cached by Glide (while loading it for the first fragment).

I took a look at the code, but I must admit that it has become way more difficult to understand than the 2.x versions.

Any help would be appreciated.

PS. I know you are trying hard to make the Glide library more extendable, but I think it has gotten far too complicated for an outsider to get a decent idea about what is going on in there; I am also afraid that not all class names are representative of their functionality, which makes it even more hard to decipher. (*no offense*, I truly appreciate your work).

rwcl...@gmail.com

unread,
Jun 12, 2015, 4:34:18 AM6/12/15
to glidel...@googlegroups.com, ble...@gmail.com
Hi Sam,

I want to only load images from the disk cache only and fail if the image isn't present in the disk cache.

The image is first loaded by:

Glide.with(activity)
       
.load(urlStr)
       
.diskCacheStrategy(DiskCacheStrategy.ALL)
       
.into(imageView);




Then in another activity, I tried to load the image from cache using a SteamModelLoader  like this

Glide.with(activity)
       
.using(new StreamModelLoader<String>() {
           
@Override            
           
public DataFetcher<InputStream> getResourceFetcher(String glideDrawable, int i, int i1) {
               
return null;
           
}
       
})
       
.load(urlStr)
       
.diskCacheStrategy(DiskCacheStrategy.ALL)
       
.into(imageView);


But the image does not show up.  Did I do something wrong?  I am using the Glide 3.6.0.  Thank you.

Róbert Papp

unread,
Jun 12, 2015, 7:07:58 AM6/12/15
to Goofyz Leung, glidel...@googlegroups.com

Hi Goofyz,
That's an interesting use of Glide! The image doesn't show up, so that's a half win, it means that it didn't use network :)
Tip: use different placeholder/fallback/error drawables while in dev. It's easier to see what's going on than "no image" for every scenario.

The other half is to get it show when cached: to find out why it doesn't turn on logging for the Engine tag and check the cache key, they must match:  https://github.com/bumptech/glide/wiki/Debugging-and-Error-Handling

Here's an example how that helps: https://github.com/bumptech/glide/issues/468

Feel free to ask more if this is not enough.
+Robi

rwcl...@gmail.com

unread,
Jun 12, 2015, 10:30:59 AM6/12/15
to glidel...@googlegroups.com
Thank you Robi,

There is NullPointerException when getting the cache key.  After some testing I found that the StreamModelLoader need to return a DataFetcher with a valid getId() method.

So for anyone who want to do similar things, you can refer to the below code.  It will load the images from cache, and do nothing if no cache exsit.

Glide.with(activity)
       
.using(new StreamModelLoader<String>() {
           
@Override
            public DataFetcher<InputStream> getResourceFetcher(final String model, int i, int i1) {
               
return new DataFetcher<InputStream>() {
                   
@Override
                    public InputStream loadData(Priority priority) throws Exception {
                       
throw new IOException();
                   
}

                   
@Override
                    public void cleanup() {

                   
}

                   
@Override
                    public String getId() {
                       
return model;
                   
}

                   
@Override
                    public void cancel() {

                   
}
               
};
           
}
       
})
       
.load(urlStr)
       
.diskCacheStrategy(DiskCacheStrategy.ALL)
       
.into(imageView);

joar...@gmail.com

unread,
Nov 18, 2015, 8:41:34 AM11/18/15
to Glide, rwcl...@gmail.com
Thanks. This works for me. I created a gist out of it:

...
Reply all
Reply to author
Forward
0 new messages