Does anybody know if it is possible to clear the cached image from a single NetworkImageView using Googles Volley library?
I have an avatar image in a NetworkImageView and would like to show the new image once I have uploaded it to the server. At the moment if I do
profileImg.setImageUrl("myUrl", mImageLoader); I get the cached image.
Check this out :
1) Turning off cache :
If you want disable the cache for a particular url, you can use setShouldCache() method as below.
StringRequest stringReq = new StringRequest(....);
stringReq.setShouldCache(false);
2) Deleting cache for particular URL : Use remove() to delete cache of an URL.
yourRequestQueue.getCache().remove(url);
3) Deleting all the cache :
yourRequestQueue.getCache().clear(url);
Also, check out this link here.
Hope this helps.
So to belatedly answer my own question. I ended up taking a different approach to ensure that I always get the latest copy of an avatar image and it turned out to be very simple.
Instead of trying to clear out an individual image from the cache I use the following method.
int time = (int) (System.currentTimeMillis());//gets the current time in milliseconds
String myUrl = "www.mySite.com?timestamp=" + String.ValueOf(time);
profileImg.setImageUrl("myUrl", mImageLoader);
What this does is to append an imaginary parameter to the url I call to get the image with a current timestamp. This guarantees that I am always making a call to a unique url and so therefore am always getting the most up-to-date version of that image.
The beauty of this approach is that it is not limited to Volley and can be used however you choose to make network calls.
in the LruBitmapCache you should do diable put(url, bitmap) :
#Override
public void putBitmap(String url, Bitmap bitmap) {
// put(url, bitmap);
}
by this way every time you call the volley method image don't save to catch
To turn off the cache:
1
request.setShouldCache(false);
to remove the cache for a specific request:
1
queue.getCache().remove(url);
to clear all cache:
1
queue.getCache().clear();
to invalidate the cache: this will allow to display the cached data until the response is received. When the response is received, it will automatically override the cached data.
1
queue.getCache().invalidate(url, true);
for more details you can refer to the
url:http://androidresearch.wordpress.com/2014/02/01/android-volley-tutorial/
If you create the cache like this:
RequestQueue requests = Volley.newRequestQueue( context );
BitmapCache cache = new BitmapCache(cacheSize);
ImageLoader loader = new ImageLoader( requests, cache );
You can clear all like this:
public void clear () {
cache.evictAll();
requests.getCache().clear();
}
I have created volleySingleton Class. I load image from ImageLoader . I have a requirement of deleting the cache but not able do that. Used all these three method
1) Deleting cache for particular URL : Use remove() to delete cache of an URL.
VolleySingleton.getInstance().getRequestQueue().getCache().remove(ImageUrl);
2) Deleting all the cache :
VolleySingleton.getInstance().getRequestQueue().getCache().clear();
3)
VolleySingleton.getInstance().getRequestQueue().getCache().invalidate(ImageUrl,true);
I have tried all of them but cashe is not getting deleted
Related
I'm developing an Android application I have implemented a custom listview with a header (ImageView) and a content (different textview), all data the data are obtained from a json that contais all text and a link that directs to an image.
I have to implement a function that download images in a local folder and instead of downloading them from the internet to fetch from the local folder.
I have to problem :
1) I have to implement a function that download in local storage the images during the parsing of my JSON. If the parser found an image must be downlaoded.
2) After the parsing the downloaded images must be associated to the local images.
3) When i try to show all the data, the images must be showed from local.
(Actually I use volley to load images from internet)
How i can do this ? How i can maintain the link beetween images and data
[
{
"date":"MY DATE",
"desc":"My description",
"id":"1",
"img":"http:\/\/MYURL\/FOLDER\/homeone.jpg",
"text":" My text",
"title":"My title"
}
]
***** EDIT 1 *****
I have a problem with Picasso library, I don't understand how I can implement the cache. For example, after the parsing of my JSON I can get the url of my JSON item.
In this case http:\/\/MYURL\/FOLDER\/homeone.jpg now I have to save the image to he local storage.
Picasso.with(this)
.load(url)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView);
This is the code that I have found in another question on StackOverflow, this could help me to create the cache ? If yes, how i can specify the cache folder ?
Is is possible only save the images on local storage ? This cache is "permanent" or all data will deleted when the application is re-started ?
What you are trying to achive is implemented in Picasso
Try to avoid wheel inventing :)
UPD:
Picasso cache images automatically and you don't need to care about this thing at all. However, you can try this example to setup cache manuallly.
This is how you can define your cache with a help of OkHTTP:
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(connectTimeOut, TimeUnit.SECONDS);
client.setReadTimeout(readTime, TimeUnit.SECONDS);
File cachePath = FuncFileDownload.getStoragePath(context, "pre");
client.setCache(new com.squareup.okhttp.Cache(cachePath, 30000000));
sPicasso = new Picasso.Builder(context)
.downloader(new OkHttpDownloader(client))
.build();
Picasso.setSingletonInstance(sPicasso);
Scenario:
I have a large GIF image which I want to cache the first time user opens the app using Glide - Image Loading and Caching library. After that whenever user opens the app, I want to show the cached version if present. This GIF URL will expire after a given interval. When it expires, I fetch the new GIF URL and display/cache that for future use.
What I tried:
I went through Caching and Cache Invalidation on Glide's github page. I also went though the Google Group thread Ensuring That Images Loaded Only Come From Disk Cache, which shows how to get the image form cache. I also went through How to invalidate Glide cache for some specific images question.
From the links above I see the following code sniplet which shows how to load the image from cache. However this only tries to get the image from cache. If its not present in cache, it doesn't try to get from the network and fails:
Glide.with(TheActivity.this)
.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("http://sampleurl.com/sample.gif")
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(theImageView);
Questions:
Is there a cleaner way to achieve the following: Show the GIF image from the cache if present else download the GIF, cache it for later use and show it in the ImageView.
The caching article above mentions the following:
In practice, the best way to invalidate a cache file is to change
your identifier when the content changes (url, uri, file path etc)
The server sends a different URL to the app when the previous one expires. In this case, I believe the old image will eventually be Garbage Collected? Is there a way to force remove the image from the cache?
On a similar note, is there a way to prevent the Garbage Collection of an image with specific key (to prevent downloading the large file again) and then later instruct to delete the old image from cache when the URL changes?
You don't need a custom ModelLoader to show the GIF from cache if present and fetch it otherwise, that's actually Glide's default behavior. Just using a standard load line should work fine:
Glide.with(TheActivity.this)
.load("http://sampleurl.com/sample.gif")
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(theImageView);
Your code will prevent Glide from downloading the GIF and will only show the GIF if it is already cached, which it sounds like you don't want.
Yes, the old image will eventually be removed. By default Glide uses an LRU cache, so when the cache is full, the least recently used image will be removed. You can easily customize the size of the cache to help this along if you want. See the Configuration wiki page for how to change the cache size.
Unfortunately there isn't any way to influence the contents of the cache directly. You cannot either remove an item explicitly, or force one to be kept. In practice with an appropriate disk cache size you usually don't need to worry about doing either. If you display your image often enough, it won't be evicted. If you try to cache additional items and run out of space in the cache, older items will be evicted automatically to make space.
Glide.with(context)
.load("http://sampleurl.com/sample.gif")
.skipMemoryCache(true)
.into(imageView);
You already noticed that we called .skipMemoryCache(true) to specifically tell Glide to skip the memory cache. This means that Glide will not put the image in the memory cache. It's important to understand, that this only affects the memory cache! Glide will still utilize the disk cache to avoid another network request for the next request to the same image URL.for more read this
Glide Cache & request optimization.
Happy coding!!
I have an app that loads images on each item on a list view, and I use Volley to make life easier for me; I need to have to images loaded from disk if it's already been downloaded before.
Problem: It won't work. It needs to re-download the images all over again. I need to have the image saved even after I exit the app.
Weird: It works only on one particular image (and it has nothing to do with size)!
What I Used: I patterned this using this site: https://github.com/rdrobinson3/VolleyImageCacheExample.
I also tried this: http://howrobotswork.wordpress.com/2013/06/02/downloading-a-bitmap-asynchronously-with-volley-example/
The Code:
String godzilla = "http://vineland.pynchonwiki.com/wiki/images/c/cf/Godzilla.jpg";
//String meme = "http://upload.wikimedia.org/wikipedia/en/d/d7/Meme_Many_Journeys.jpg";
ImageCacheManager.getInstance().getImageLoader().get(godzilla, new ImageLoader.ImageListener() {
#Override
public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) {
viewHolder.backgroundImage = imageContainer.getBitmap();
updateBackgroundImage(viewHolder, viewHolder.backgroundImage, object);
updateLayoutAlignmentParams(viewHolder);
}
#Override
public void onErrorResponse(VolleyError volleyError) {
}
});
if(viewHolder.backgroundImage != null)
updateBackgroundImage(viewHolder, viewHolder.backgroundImage, object);
I've tried the meme website, and it still has problems. I had one particular site that contains an image that oddly works. Which makes it even more confusing.
Edit: Additional info, it seems like there's an error on adding lruEntries as lruEntries.remove(entry.key is being called on completeEdit().
Volley has 2 cache layers when it comes to images:
The L1 level: a memory cache provided by you in the ImageLoader constructor.
The L2 level: a disk cache that is shared among every request performed by the same RequestQueue.
The disk cache caches every response unless explicitly requested not to by the request. But, the caching is performed according to the HTTP cache headers of the response.
When you request an image, this is what Volley does:
Check L1 cache for image. Return if found.
Image not in memory - Check L2 cache. If found check expiration of the cache headers. If still valid, add to L1 cache and return image.
Image not on disk (either not there or expired) - Perform a network request. Cache the response in the disk cache and the bitmap in the memory cache and return.
I bet that the image that loaded from the disk has cache headers.
IMO, you have 3 options:
The image server is yours - add the appropriate cache headers.
The image server isn't yours - accept the fact the some images will not be cached on the disk.
Override the caching policy to better suit your needs. This means editing the Volley source code.
I load an image from disk using Picasso, e.g., Picasso.with(ctx).load(new File("/path/to/image")).into(imageView), but whenever I save a new image in that file, and refresh my ImageView, Picasso still has the bitmap cached.
Is it possible to invalidate the cache in Picasso?
In the recent versions of Picasso, there is a new method for invalidate, without any workarounds, so I think that custom PicassoTools class mentioned earlier, is now obsolete in this case
Picasso.with(getActivity()).invalidate(file);
Actually, based on your own answer, there is an easier way to do it without forking the library. Add this class to the com.squareup.picasso package.
package com.squareup.picasso;
public class PicassoTools {
public static void clearCache (Picasso p) {
p.cache.clear();
}
}
Because cache has package visibility, this util class can clear the cache for you. You just have to call it:
PicassoTools.clearCache(Picasso.with(context));
Abort memory cache and disk cache check by indicate memory policy by flag: emoryPolicy.NO_CACHE and NetworkPolicy.NO_CACHE as below code snippet:
mPicasso.with(mContext)
.load(url)
.memoryPolicy(MemoryPolicy.NO_CACHE )
.networkPolicy(NetworkPolicy.NO_CACHE)
.resize(512, 512)
.error(R.drawable.login)
.noFade()
.into(imageView);
Try to use:
Picasso.with(ctx).load(new File("/path/to/image")).skipMemoryCache().into(imageView)
The order of search image in Picasso is:
Memory cache -> Disk cache -> Network
So there are few scenario we need to invalidate cache in Picasso:
1.Invalidate memory cache:
Usercase: When image already update in disk cache or remote host
Solution: Clear cache of Url, File, Uri if exist
mPicasso.with(appContext).invalidate(File);
mPicasso.with(appContext).invalidate(Url);
mPicasso.with(appContext).invalidate(Uri);
.
2.Invalidate memory cache and disk cache Online
※note: Online mean update directly to ImageView
User case: Image updated on remote host
Solution: Abort image on memory cache and disk cache then request image on remote host
mPicasso.with(appContext)
.load(url)
.memoryPolicy(MemoryPolicy.NO_CACHE )
.networkPolicy(NetworkPolicy.NO_CACHE)
.into(imageView);
-> Abort memory cache and disk cache
.
3.Invalidate memory cache and disk cache Offline
※ note: Offline mean update not update to ImageView, just background fetch to using later
User case: You know image on remote host updated, but only want to update cache only to using afterward (not update into image view)
Solution: fetch only
mPicasso.with(appContext)
.load(url)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.networkPolicy(NetworkPolicy.NO_CACHE)
.fetch();
※Note: Using fetch() is good but it also consume network resource, so please consider carefully, check scenario 4 in below for better solution
4.Invalidate memory cache and disk cache Offline if disk cache is exist
User case: Only invalidate cache if already exist in disk cache
Solution: Should check disk by using parameter: NetworkPolicy.OFFLINE cache before fetch
mPicasso.with(appContext)
.load(url)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.networkPolicy(NetworkPolicy.OFFLINE)
.fetch(new Callback() {
#Override
public void onSuccess() {
//Success: mean disk cache exist -> should do actual fetch
picasso.load(url).fetch();
}
#Override
public void onError() {
//Failed: mean disk cache not exist
}
});
Picasso is an amazing libs, I hope squareup will add more convenience API to manage cache in upcoming future.
Another option is to delete the cache directory itself, for example on app startup:
deleteDirectoryTree(context.getCacheDir());
where:
/**
* Deletes a directory tree recursively.
*/
public static void deleteDirectoryTree(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
for (File child : fileOrDirectory.listFiles()) {
deleteDirectoryTree(child);
}
}
fileOrDirectory.delete();
}
That deletes the whole cache directory, which is fine if you want to simulate first-use of your app. If you only want to delete the Picasso cache, add "picasso-cache" to the path.
What you can do if you want to delete all cache at once, is to create a custom Picasso.LruCache, and then use the clear method on it.
Here is a sample:
Picasso.Builder builder = new Picasso.Builder(this);
LruCache picassoCache = new LruCache(this);
builder.memoryCache(picassoCache);
Picasso.setSingletonInstance(builder.build());
To clear the cache:
picassoCache.clear();
You can clear image cache of picasso by setting your own cache and clear that.
This code was tested on Picasso 2.5.0
private Picasso picasso;
private LruCache picassoLruCache;
picassoLruCache = new LruCache(context);
// Set cache
picasso = new Picasso.Builder(context) //
.memoryCache(picassoLruCache) //
.build();
// Clear cache
picassoLruCache.clear();
Doesn't loop pretty, but this approach fixed my issue with cache and Picasso. Only use this when you want to invalidate the cache for a specific URL, this approach is slow and probably is not the correct way of doing but works for me.
String url = "http://www.blablabla.com/Raiders.jpg";
Picasso.with(this).invalidate(url);
Picasso.with(this)
.load(url)
.networkPolicy(
NetworkUtils.isConnected(this) ?
NetworkPolicy.NO_CACHE : NetworkPolicy.OFFLINE)
.resize(200, 200)
.centerCrop()
.placeholder(R.mipmap.ic_avatar)
.error(R.mipmap.ic_avatar)
.into(imageView);
A very important part is missing from the accepted answer here. I found the trick from here: http://blogs.candoerz.com/question/124660/android-image-cache-is-not-clearing-in-picasso.aspx
Just calling the following line, wouldn't clear the cache of a photo when you use custom options like resize, center crop etc when displaying the original image.
Picasso.with(getContext()).invalidate(file);
The solution:
When displaying the image, use stableKey() method.
Picasso.with(getContext()).load(new File(fileUri))
.skipMemoryCache()
.placeholder(R.drawable.placeholder)
.stableKey(fileUri)
.into(imageview);
Then, you can clear the cache of this file later by calling this:
Picasso.with(getContext()).invalidate(fileUri);
Hope this will help.
You can skip memory cache by skipMemoryCache()
see the following
Picasso.with(this)
.load(IMAGE_URL)
.skipMemoryCache()
.placeholder(R.drawable.placeholder)
.error(R.drawable.no_image)
.into(mImageViewPicasso);
gradle compile "com.squareup.picasso:picasso:2.4.0"
Another option is to save the new image into a different file than the original. Since the Picasso bitmap cache is keyed off of the file path, loading the new image from a different file will result in a cache miss. This also has the side benefit of not having to clear the entire cache.
use shutdown() instead
As per source code; shutdown will stop accepting further request as well as clear all cache
/** Stops this instance from accepting further requests. */
public void shutdown() {
if (this == singleton) {
throw new UnsupportedOperationException("Default singleton instance cannot be shutdown.");
}
if (shutdown) {
return;
}
cache.clear();
cleanupThread.shutdown();
stats.shutdown();
dispatcher.shutdown();
for (DeferredRequestCreator deferredRequestCreator : targetToDeferredRequestCreator.values()) {
deferredRequestCreator.cancel();
}
targetToDeferredRequestCreator.clear();
shutdown = true;
}
Also you can not shutdown singleton instance.
So you need to have instance variable for Picasso. Do not forget to reinitialize picasso instance everytime you shutdown() it in order to reuse it
File f = new File(path, name);
Picasso.with(this).invalidate(Uri.fromFile(f));
I'm trying to display a listview with a lot of (remote) images. I'm trying to use volley for the task.
Volley somewhat works, but not good enough. In ImageLoader.get volley has the following piece of code:
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
// Try to look up the request in the cache of remote images.
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
if (cachedBitmap != null) {
// Return the cached bitmap.
ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
imageListener.onResponse(container, true);
return container;
}
However, getCacheKey produces a key like this:
/**
* Creates a cache key for use with the L1 cache.
* #param url The URL of the request.
* #param maxWidth The max-width of the output.
* #param maxHeight The max-height of the output.
*/
private static String getCacheKey(String url, int maxWidth, int maxHeight) {
return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
.append("#H").append(maxHeight).append(url).toString();
}
i.e. It appends some "metadata" like width and height to the key.
This key never produces a hit and if the image is not in the L1 cache it is fetched online. When the image is fetched online it is saved in the disk-cache but Volley saves it with the URL (and only the URL) as key.
Is this expected behaviour? Am I missing something?
The reason you're not getting any hits is because the default behavior in Volley for disk caching is dependent on the HTTP headers of the element you're requesting (in your case, an image).
The way Volley works is:
ImageLoader checks the L1 cache for image (memory cache provided by you to the ImageLoader in its constructor). If available return image.
Request processed by RequestQueue. It checks the L2 (disk cache) for the image.
If found in the disk cache, check the image expiry time. If not expired, return.
Download image and return it.
Save image in caches.
If you want the default settings to work, the images must have a Cache-Control header like max-age=??? where the question marks indicate enough seconds from the time it was downloaded.
If you want to change the default behavior, I'm not sure, but I think you have to edit the code a bit.
Look at the CacheDispatcher class in the Volley source.
Can you post your class that implements ImageCache.
I've just been looking at this myself and realised in my code I wasn't adding the bitmap to the memory cache when it was loading it from the disk, so it would always reload it from the disk each time.
This is a simple example of what I mean and where I was going wrong
#Override
public Bitmap getBitmap(String cachKey) {
Bitmap b = null;
//check the memory first
b = memoryCache.get(cacheKey);
if(b == null){
//memory cache was null, check file cache
b = diskLruImageCache.getBitmap(cacheKey);
// this is where it needs to be added to your memory cache
if(b != null){
memoryCache.put(url, b);
}
}
return b;
}
I tracked down this issue in my own app today. I was setting a max cache size in KB in the constructor, but reporting a size in bytes in sizeOf(), so nothing was ever cached.
This answer set me straight.
Probably you are using NetworkImageView to load your images. You can use a ImageView and ImageLoader to do the same thing. Using ImageLoader the metadata in the key is like "#W0#H0" for any image size.
ImageLoader imageLoader = getImageLoader();
imageLoader.get(url, ImageLoader.getImageListener(imageView, defaultDrawable, errorDrawable));
Volley wont cache anything, if cache control is not set in the response header.
Check the HttpHeaderParser class implementation in Volley.
Caching can be based on max-age or E-tag. Check your response header and identify anything set there. It will look something like this.
Cache-Control → public, max-age=300
Cache Header info
This is the exact way you want it to work.
Hit the url and get the image when its not available.
Load the image from the cache if available.