Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update cache / force to load via network #463

Closed
zasunkx opened this issue May 14, 2015 · 7 comments
Closed

Update cache / force to load via network #463

zasunkx opened this issue May 14, 2015 · 7 comments
Labels

Comments

@zasunkx
Copy link

zasunkx commented May 14, 2015

I have constant ImageURLs in my application with changing content (Bitmaps) but the client doesn't know when those images are updated. Now, I want those images to load the following way:

  1. First, display cached version of mImageURL (works)
  2. Once the image is displayed (or onResourceReady, which is good enough) update the cache of mImageURL by forcing Glide to download the image of mImageURL via network.
  3. Display the new updated image

Desired effect:
Case 1 - the content of mImageURL didn't change since last download: The image is displayed immediately (step 1) and is replaced by an updated version later (step 3), but as both images are identical the replacement won't be noticable.
Case 2 - the content of mImageURL did change: At first the old image (step 1) pops up, but after the cache was updated it is replaced by the new image a little later (step 3).

How can I achieve this?
I tried to implement step 2 with .downloadOnly() as I thought that would enforce loading from network, but apparently it didn't. The FutureTarget contained the same old bitmap as in step 1 although the content of mImageURL changed. I didn't find any other way to explicity load from network (but save in cache), nor to invalidate the cache of an url. I couldn't think of a way signatures could solve my problem either.

@sjudd
Copy link
Collaborator

sjudd commented May 15, 2015

The API doesn't directly support this use case, but you can probably get what you want using the thumbnail and signature APIs.

You can use an incrementing number as your signature and save the value for the last successful fetch in a database or shared preferences. Then you load your last known good value as your thumbnail, and the same request with an incremented value as the full request. The thumbnail will load the image from cache and display it, then the full request will fetch the image, cache it again, and replace the thumbnail. You can either use a custom Target or a RequestListener to save the incremented value when the full request completes.

Your request might look something like this:

Glide.with(fragment)
    .load(url)
    .signature(new StringSignature(lastKnownGoodValue + 1))
    .thumbnail(
        Glide.with(fragment)
            .load(url)
            .signature(new StringSignature(lastKnownGoodValue)))
    .into(imageView);

Thumbnails are loaded with higher priority, but only displayed until their parent completes, at which point they are replaced by the result of the parent load (if it is successful).

For what it's worth, this is somewhat wasteful. If you have control over your backend, it might be more performant to make a separate metadata only request for the state of your urls rather than downloading the images each time.

@sjudd sjudd added the question label May 15, 2015
@TWiStErRob
Copy link
Collaborator

Just throwing things out there.

@zasunkx It should also be possible to somehow harness HTTP 304 Not Modified, if your server supports it. It may require you to write a clever model/decoder/cache.

@sjudd Would it be possible to support 304 inside the stock decoder and/or as a feature to allow generic external (over the wire) "Did this resource change?" questions? I guess the problem is that the decoder is not even consulted becuse we have the resource in cache, but there's no expiry for it, because it's LRU. This may sound like an async signature....

@vipinhelloindia
Copy link

I really don't know how Glide handle caching of Images
But one can use

Cache-Control: private, max-age=0, no-cache
Cache-Control: private, max-age=0, proxy-revalidate
Cache-Control: private, max-age=0,no-store
Cache-Control: private, max-age=0,must-revalidate

Date:serverDate
Expires:expiryDate
ETag:ETag for cache coherency
Last-Modified : Some server return ;

You can define max age in Header , so next time Glide should check validate and update accordingly , and if the server return 304 then Glide should pick from device cache or else update it .

Volley has better managed HTTP caching .I have not looked into Glide Implementation thought Glide support Volley Integration as well

@TWiStErRob
Copy link
Collaborator

@vipinhelloindia I think this is close to what I asked sjudd if it's possible to incorporate this in a generic way. Currently you have to do it yourself as described above.

Also my understanding is that Glide uses the image in cache if it's cache key is matched, otherwise not. As far as I know there's no internal handling of those HTTP headers. You can set cache-control all you like, but once an image gets into Glide's cache, it'll reuse it. Glide won't even attempt to connect to the endpoint again (i.e. won't call DataFetcher methods) like a browser would to revalidate if the cached file is not too old. However the cache key is a really dynamic thing with fine control over what it will become in the end and there are ways around this.

Some time ago in one of the issues it was stated that Glide is an image loading library not a networking library. You can howerer replace the networking part via integration libraries. If you think Volley has what you need, you can try setting up that as a cache and disabling Glide's cache (DiskCacheStrategy.NONE), but this needs some evaluation of priorities and side effects, proceed with much caution.

@sjudd
Copy link
Collaborator

sjudd commented May 24, 2015

@TWiStErRob Sorry I missed your comment earlier.

If you want http caching, you can absolutely use Volley, HttpUrlConnection, or OkHttp with caching and use DiskCacheStrategy.NONE in Glide. You could even probably find a way to build this in using the interfaces Glide provides, although I'm not sure there's a clean way to do so. If you do come up with a way to implement this, I'd definitely consider a contribution.

That said, as @TWiStErRob mentioned, Glide is an image loading library. I'm trying to do my best to avoid building a network library underneath, especially because there are so many good alternatives.

From a more philosophical perspective, I also have a hard time believing that relying on http caching or 304s provides a great experience for users. My understanding here might be flawed, but it seems that relying on 304s gives you two bad options:

  1. Always wait for the result of the 304 before decoding the image.
  2. Decode the cached image, and then do the 304 and update if necessary.

In the first case you're taking a decode time that usually takes milliseconds or tens of milliseconds and subjecting it to an RPC that may take hundreds or thousands of milliseconds.
In the second case your user sees one image and then soon after a completely different image.

Ideally you use unique urls for each image and use separate RPCs or GCM messages to notify the app when those images/urls change.

@TWiStErRob
Copy link
Collaborator

@sjudd, completely different seems a little stretch. I would imagine that having the same url would at least relate to the displayed entity in some way and would be completely different all the time. I could think of these two use cases where this would be helpful:

  1. profile picture: the user would change their image (upload a new one) which is displayed on forum posts for example and it would probably require a lot of extra logic to let all the clients know that a particular profile image has changed, in which case your bad-option 2 can be useful. Of course the alternative of sending the profile image along with each post is a good thing as then a new url could contain the hash of the new image.
  2. generated picture where maybe a watermark (like location or ranking) changes. This is similar to profile pictures where some changing info is generated on the image. An example could be mini profile image on a travel social network where the users current city is displayed as watermark, but the image's url stays /user/123456/profileImage. Of course in this case a regular 1 day signature can be used so the update is near real time, but bad-option two gives a nearer real time experience as the image probably doesn't change much.

@sjudd
Copy link
Collaborator

sjudd commented Jun 13, 2015

I see the point, but ultimately if you want http caching, you should use an http library. We've tried to make it super simple to integrate nicely with pretty much any networking library you can think of. Volley in particular implements http caching and we have a fairly robust default implementation for it.

I'm definitely open to ideas that make caching more robust, or integrating with libraries easier (ie setting a default disk cache strategy).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants