I want to prefetch images using Picasso and save them all to disk upon opening the app (no lazy loading on the fly). To make sure the cache is big enough I am using the following code in my Application onCreate
ContextWrapper cw = new ContextWrapper(getApplicationContext());
// path to /data/data/yourapp/app_data/imageDir
File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
OkHttpClient client = new OkHttpClient.Builder()
.cache(new Cache(directory, Integer.MAX_VALUE))
.build();
OkHttp3Downloader okHttp3Downloader = new OkHttp3Downloader(client);
Picasso.Builder builder = new Picasso.Builder(this);
builder.downloader(okHttp3Downloader);
Picasso built = builder.build();
built.setIndicatorsEnabled(false);
built.setLoggingEnabled(false);
Picasso.setSingletonInstance(built);
So here I set my cache size to Integer.MAX_VALUE which should be big enough ;)
I use code similar to this line to prefetch the image: Picasso.with(context).load(url).fetch();.
Now when I kill my internet and mobile data, no images are loaded even though the my code is fetching items. Any ideas why?
Maybe the problem is loosing state. Did you try to extend Application class (like this) and keep Picasso instance here?
In similar situation when I needed to fetch lots of images, I save them to internal memory (not just in some cache) - to prevent from reloading, and save uri's to files into SQLite and then use Picasso to work with such URI's. (don't forget to check that user don't delete anything).
In the end I created my own image manager that downloads and store images (still using picasso to download them).
Here is my approach:
In Application's onCreate method do:
Picasso picasso = new Picasso.Builder(this)
.downloader(new OkHttp3Downloader(this,Integer.MAX_VALUE))
.build();
picasso.setIndicatorsEnabled(BuildConfig.DEBUG);
picasso.setLoggingEnabled(BuildConfig.DEBUG);
Picasso.setSingletonInstance(picasso);
And then wherever you want to load your images call this for each image's url:
Picasso.with(context).load(url).fetch();
From doc:
fetch()
Asynchronously fulfills the request without a ImageView or Target. This is useful when you want to warm up the cache with an image.
You can even pass in callback to check for possible errors
I have to use custom OkHttpClient so I can add headers to the image requests. The problem is Picasso won't cache any images on disk because of this. I've used setIndicatorsEnabled(true) to check caching and I see only red indicators. When I use default OkHttpDownloader all is ok. Below is my Picasso initialization code. So does anyone encounter the same problem?
public static void init(Context context) {
Picasso.Builder builder = new Picasso.Builder(context);
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new AuthInterceptor());
Downloader downloader = new OkHttpDownloader(client);
Picasso.setSingletonInstance(builder.downloader(downloader).build());
Picasso.with(context).setIndicatorsEnabled(true);
}
Also my image download code
public static void load(final ImageView imageView, final Image image) {
Picasso.with(imageView.getContext())
.load(image.getUrl())
.resize(400, 0)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.into(imageView);
}
Ah since this is happening when you change headers, you are most probably not setting the Cache-Control header
According to Jake wharton (One of the developer of Picasso)
Picasso doesn't have a disk cache. It delegates to whatever HTTP
client you are using for that functionality (relying on HTTP cache
semantics for cache control). Because of this, the behavior you seek
comes for free
Taken from Jake Wharton's answer here
Also,
If you never see a blue indicator, it's likely that your remote images
do not include proper cache headers to enable caching to disk
Im using Picasso and okhttp to download and cache images onto the disk.
Im using the below code snippet to set a cache of 10MB to the okhttp client and using it for Picasso.
File folder = new File(context.getCacheDir(),"HomePageCache");
if (!folder.exists())
folder.mkdir();
okHttpClient.setCache(new com.squareup.okhttp.Cache(folder, 1024 * 1024 * 10));
OkHttpDownloader okHttpDownloader = new OkHttpDownloader(okHttpClient);
picasso = new com.squareup.picasso.Picasso.Builder(context).indicatorsEnabled(true).downloader(okHttpDownloader).build();
This is working perfectly most of the time caching and reloading images even when offline , but the problem is that sometimes it just clears the image cache completely.
I am checking the size of the image cache just to be sure, and it has never crossed 2 MB.
Is there any other reason why my cache is getting cleared?
Like Nikola pointed out in the comment, the cache expire header was responsible for invalidating cached images.
i'm using volley to load my images and cache them.
mImageLoader = new ImageLoader(getRequestQueue(context), mImageCache);
which mImageCache is a DiskLruImageCache.
volley fetches images from server by ImageRequest which extend the ImageRequest<Bitmap>
and in request class there is boolean that defines whether to cache the response or not
/** Whether or not responses to this request should be cached. */
private boolean mShouldCache = true;
and ImageRequest hasn't disabled mShouldCache.
as you can see the default value is true so after volley fetches an image caches it under the volley cache directory by diskBasedCache.
so now i have to cache bitmap one from ImageRequest and one from ImageLoader how can i disable ImageRequest cache ? or any other suggestions ?
You are making a mistake giving the ImageLoader a disk cache. Volley already has a shared disk cache for every response, be it an image are not, that works according to HTTP cache headers by default.
You are supposed to provide a memory bitmap cache to the ImageLaoder. Look at the documentation.
The reasoning for it is how Volley is designed. This is the image request logic for Volley:
Image with url X is added to the queue.
Check image memory cache (provided by you) - If available, return bitmap. quickest
Check shared disk cache - If available, check cache headers to see that image is still valid. If valid - add to memory bitmap cache and return. slower, but still pretty quick
This step means that either the image was in the disk cache but its cache headers are missing or expired, or the image wasn't available in the cache at all. Either way, Volley performs a network request and caches the response in both caches. slowest
So by providing a disk cache - you are both slowing down your app and taking up to twice as much disk space with redundant image saving.
Use a memory cache.
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));