How do I use disk caching in Picasso?

116,897

Solution 1

This is what I did. Works well.

First add the OkHttp to the gradle build file of the app module:

compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp3:okhttp:3.10.0'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'

Then make a class extending Application

import android.app.Application;

import com.jakewharton.picasso.OkHttp3Downloader;
import com.squareup.picasso.Picasso;

public class Global extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Picasso.Builder builder = new Picasso.Builder(this);
        builder.downloader(new OkHttp3Downloader(this,Integer.MAX_VALUE));
        Picasso built = builder.build();
        built.setIndicatorsEnabled(true);
        built.setLoggingEnabled(true);
        Picasso.setSingletonInstance(built);

    }
}

add it to the Manifest file as follows :

<application
        android:name=".Global"
        .. >

</application>

Now use Picasso as you normally would. No changes.

EDIT:

if you want to use cached images only. Call the library like this. I've noticed that if we don't add the networkPolicy, images won't show up in an fully offline start even if they are cached. The code below solves the problem.

Picasso.with(this)
            .load(url)
            .networkPolicy(NetworkPolicy.OFFLINE)
            .into(imageView);

EDIT #2

the problem with the above code is that if you clear cache, Picasso will keep looking for it offline in cache and fail, the following code example looks at the local cache, if not found offline, it goes online and replenishes the cache.

Picasso.with(getActivity())
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView, new Callback() {
    @Override
    public void onSuccess() {

    }

    @Override
    public void onError() {
        //Try again online if cache failed
        Picasso.with(getActivity())
                .load(posts.get(position).getImageUrl())
                .error(R.drawable.header)
                .into(imageView, new Callback() {
            @Override
            public void onSuccess() {

            }

            @Override
            public void onError() {
                Log.v("Picasso","Could not fetch image");
            }
        });
    }
});

Solution 2

1) answer of first question : according to Picasso Doc for With() method

The global default Picasso instance returned from with() is automatically initialized with defaults that are suitable to most implementations.

  • LRU memory cache of 15% the available application RAM
  • Disk cache of 2% storage space up to 50MB but no less than 5MB.

But Disk Cache operation for global Default Picasso is only available on API 14+

2) answer of second Question : Picasso use the HTTP client request to Disk Cache operation So you can make your own http request header has property Cache-Control with max-age And create your own Static Picasso Instance instead of default Picasso By using

1] HttpResponseCache (Note: Works only for API 13+ )
2] OkHttpClient (Works for all APIs)

Example for using OkHttpClient to create your own Static Picasso class:

  • First create a new class to get your own singleton picasso object

    import android.content.Context;
    import com.squareup.picasso.Downloader;
    import com.squareup.picasso.OkHttpDownloader;
    import com.squareup.picasso.Picasso;
    
    public class PicassoCache {
    
        /**
         * Static Picasso Instance
         */
        private static Picasso picassoInstance = null;
    
        /**
         * PicassoCache Constructor
         *
         * @param context application Context
         */
        private PicassoCache (Context context) {
    
            Downloader downloader   = new OkHttpDownloader(context, Integer.MAX_VALUE);
            Picasso.Builder builder = new Picasso.Builder(context);
                builder.downloader(downloader);
    
            picassoInstance = builder.build();
        }
    
        /**
         * Get Singleton Picasso Instance
         *
         * @param context application Context
         * @return Picasso instance
         */
        public static Picasso getPicassoInstance (Context context) {
    
            if (picassoInstance == null) {
    
                new PicassoCache(context);
                return picassoInstance;
            }
    
            return picassoInstance;
        }
    
    } 
    
  • use your own singleton picasso object Instead of Picasso.With()

PicassoCache.getPicassoInstance(getContext()).load(imagePath).into(imageView)

3) answer for third question : you do not need any disk permissions for disk Cache operations

References: Github issue about disk cache, two Questions has been answered by @jake-wharton -> Question1 and Question2

Solution 3

For caching, I would use OkHttp interceptors to gain control over caching policy. Check out this sample that's included in the OkHttp library.

RewriteResponseCacheControl.java

Here's how I'd use it with Picasso -

OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.networkInterceptors().add(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Response originalResponse = chain.proceed(chain.request());
            return originalResponse.newBuilder().header("Cache-Control", "max-age=" + (60 * 60 * 24 * 365)).build();
        }
    });

    okHttpClient.setCache(new Cache(mainActivity.getCacheDir(), Integer.MAX_VALUE));
    OkHttpDownloader okHttpDownloader = new OkHttpDownloader(okHttpClient);
    Picasso picasso = new Picasso.Builder(mainActivity).downloader(okHttpDownloader).build();
    picasso.load(imageURL).into(viewHolder.image);

Solution 4

For the most updated version 2.71828 These are your answer.

Q1: Does it not have local disk cache?

A1: There is default caching within Picasso and the request flow just like this

App -> Memory -> Disk -> Server

Wherever they met their image first, they'll use that image and then stop the request flow. What about response flow? Don't worry, here it is.

Server -> Disk -> Memory -> App

By default, they will store into a local disk first for the extended keeping cache. Then the memory, for the instance usage of the cache.

You can use the built-in indicator in Picasso to see where images form by enabling this.

Picasso.get().setIndicatorEnabled(true);

It will show up a flag on the top left corner of your pictures.

  • Red flag means the images come from the server. (No caching at first load)
  • Blue flag means the photos come from the local disk. (Caching)
  • Green flag means the images come from the memory. (Instance Caching)

Q2: How do I enable disk caching as I will be using the same image multiple times?

A2: You don't have to enable it. It's the default.

What you'll need to do is DISABLE it when you want your images always fresh. There is 2-way of disabled caching.

  1. Set .memoryPolicy() to NO_CACHE and/or NO_STORE and the flow will look like this.

NO_CACHE will skip looking up images from memory.

App -> Disk -> Server

NO_STORE will skip store images in memory when the first load images.

Server -> Disk -> App
  1. Set .networkPolicy() to NO_CACHE and/or NO_STORE and the flow will look like this.

NO_CACHE will skip looking up images from disk.

App -> Memory -> Server

NO_STORE will skip store images in the disk when the first load images.

Server -> Memory -> App

You can DISABLE neither for fully no caching images. Here is an example.

Picasso.get().load(imageUrl)
             .memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)
             .networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
             .fit().into(banner);

The flow of fully no caching and no storing will look like this.

App -> Server //Request

Server -> App //Response

So, you may need this to minify your app storage usage also.

Q3: Do I need to add some disk permission to android manifest file?

A3: No, but don't forget to add the INTERNET permission for your HTTP request.

Solution 5

1) Picasso by default has cache (see ahmed hamdy answer)

2) If your really must take image from disk cache and then network I recommend to write your own downloader:

public class OkHttpDownloaderDiskCacheFirst extends OkHttpDownloader {
    public OkHttpDownloaderDiskCacheFirst(OkHttpClient client) {
        super(client);
    }

    @Override
    public Response load(Uri uri, int networkPolicy) throws IOException {
        Response responseDiskCache = null;
        try {
            responseDiskCache = super.load(uri, 1 << 2); //NetworkPolicy.OFFLINE
        } catch (Exception ignored){} // ignore, handle null later

        if (responseDiskCache == null || responseDiskCache.getContentLength()<=0){
            return  super.load(uri, networkPolicy); //user normal policy
        } else {
            return responseDiskCache;
        }

    }
}

And in Application singleton in method OnCreate use it with picasso:

        OkHttpClient okHttpClient = new OkHttpClient();

        okHttpClient.setCache(new Cache(getCacheDir(), 100 * 1024 * 1024)); //100 MB cache, use Integer.MAX_VALUE if it is too low
        OkHttpDownloader downloader = new OkHttpDownloaderDiskCacheFirst(okHttpClient); 

        Picasso.Builder builder = new Picasso.Builder(this);

        builder.downloader(downloader);

        Picasso built = builder.build();

        Picasso.setSingletonInstance(built);

3) No permissions needed for defalut application cache folder

Share:
116,897
Sowmya Madras Rajesh
Author by

Sowmya Madras Rajesh

Updated on February 18, 2021

Comments

  • Sowmya Madras Rajesh
    Sowmya Madras Rajesh over 3 years

    I am using Picasso to display image in my android app:

    /**
    * load image.This is within a activity so this context is activity
    */
    public void loadImage (){
        Picasso picasso = Picasso.with(this); 
        picasso.setDebugging(true);
        picasso.load(quiz.getImageUrl()).into(quizImage);
    }
    

    I have enable debugging and it always shows either red and green .But never shows yellow

    Now if i load same image next time and internet is not available the image is not loaded.

    Questions:

    1. Does it not have local disk cache?
    2. How do i enable disk caching as i will be using same image multiple times.
    3. Do i need to add some disk permission to android manifest file?
  • nbumakov
    nbumakov over 9 years
    No, this isn't work if app was closed. After app was force stopped all images disappeared.
  • CIRCLE
    CIRCLE over 9 years
    this is giving me this error: FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
  • ahmed hamdy
    ahmed hamdy over 9 years
    @CIRCLE sorry for late, To use Example,You need to download first [okhttp] (square.github.io/okhttp) package and [okio] (github.com/square/okio) package which used by okhttp
  • ahmed hamdy
    ahmed hamdy over 9 years
    @CIRCLE may be you need to download [okhttp-urlconnection] (mvnrepository.com/artifact/com.squareup.okhttp/…) package too
  • Sanket Berde
    Sanket Berde about 9 years
    @ArtjomB. , I did answer the question. And the solution does work. But there is this little clarification i can use. I went through the OkHttp documentation and they do not mention the unit of "cache". So if anyone would like to share some wisdom... this is a good opportunity.
  • Sanket Berde
    Sanket Berde about 9 years
    @ArtjomB. yes that makes sense. Edited !
  • Admin
    Admin almost 9 years
    @SanketBerde now i dont need to download images and check to exist in sdcard and show that into such as ImageViewok?
  • TheDevMan
    TheDevMan almost 9 years
    @SanketBerde: Thanks for the quick note but I figured out the image is coming up from memory only if the App is running in background (when on offline). if I close the app, that is clear the running apps then open my app again the images don't load from Cache. I have set the error default loading image that is coming up. What could be wrong here?
  • Sanket Berde
    Sanket Berde almost 9 years
    @TheDevMan yes even i noticed the problem.. i will post an edit supporting completely offline operation.
  • Sanket Berde
    Sanket Berde almost 9 years
    @TheDevMan the first time you load an image on the app start, it will not come from cache, if you have previously downloaded it, it will come from disk and if you haven't, then from network.
  • Zhang
    Zhang over 8 years
    I got a crash about OkHttp, I also needed to include package OkHttp-URLConnection: compile 'com.squareup.okhttp:okhttp-urlconnection:2.7.4' to my gradle.
  • Poras Bhardwaj
    Poras Bhardwaj about 8 years
    @Sanket Can I find/view these cached images on disk in Device. If yes, then in which directory? I have searched my Device File Explorer, but unable to find any cached image on disk.
  • Charu
    Charu about 8 years
    This is not working for me. Images are being reload each time when I scroll to their position in viewPager
  • ahmed hamdy
    ahmed hamdy about 8 years
    @charu you can use DEBUG INDICATORS feature in Picasso to figure image load from network or disk or memory. Call setIndicatorsEnabled(true) on the Picasso instance. for more details see official doc
  • zeeshan
    zeeshan about 8 years
    Maybe Picasso have changed how things work, because for me, it works fine without okhttp and network policy. On a new start, it instantly gets images from the disk, and when network is offline, it still shows them fine.
  • noev
    noev almost 8 years
    Doesn't work anymore networkInterceptors() returns an immutable list.
  • Gaurav B
    Gaurav B almost 8 years
    @noev in OkHttp 3.x you can use builder pattern (see github.com/square/okhttp/wiki/Interceptors) to add interceptors.
  • Weekend
    Weekend almost 8 years
    @zeeshan The solution in "EDIT #2" is dirty. According to doc of NetworkPolicy.OFFLINE, "Forces the request through the disk cache only, skipping network", that's the problem. (But it's only a guess. I haven't write a test demo.)
  • Sanket Berde
    Sanket Berde almost 8 years
    @Weekend hence the next request with no NetworkPolicy.OFFLINE in the onError() of the first call.
  • Billda
    Billda over 7 years
    Please be aware of new OkHttpDownloader(this,Integer.MAX_VALUE) part. This will set maximum size of cache to "infinity" and your app size will be increasing.
  • VKostenc
    VKostenc over 7 years
    With okhttp3.OkHttpClient library you have to use OkHttp3Downloader class form compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
  • Ali Ahmed
    Ali Ahmed about 7 years
    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/squareup/okhttp/OkHttpClient;
  • Mete
    Mete over 6 years
    With current versions, i give "Failed resolution of: Lcom/squareup/okhttp/OkHttpClient;" exception. But it works great when I use the versions you wrote.
  • Prakash
    Prakash over 6 years
    @Billda No it will not set cache to infinite. That value will be used to set the size limit for cache.
  • user25
    user25 about 6 years
    cache isn't working, I even checked private app folder and /cache/picasso-cache folder is empty. I'm trying to cache thumbnails stackoverflow.com/a/30045646/4548520
  • user25
    user25 about 6 years
    it's not available anymore (.with), from 2.7 version
  • ahmed hamdy
    ahmed hamdy about 6 years
    @user25 Ok, But I didn't use with method into my solution
  • user924
    user924 about 6 years
    OkHttp is already inside picasso why should we put it in gradle?
  • user924
    user924 about 6 years
    Picasso.with(getActivity()) .load(imageUrl) .networkPolicy(NetworkPolicy.OFFLINE) i don't see any picasso cache in app private folder
  • user924
    user924 about 6 years
    nothing works, I see annoying placeholder all the time, github.com/square/picasso/issues/1936
  • user924
    user924 about 6 years
    your solution has issues Do not place Android context classes in static fields (static reference to Picasso which has field context pointing to Context); this is a memory leak (and also breaks Instant Run)
  • user924
    user924 about 6 years
    and remove unnecessary lines like picasso.setIndicatorsEnabled(true); picasso.setLoggingEnabled(true); it's for debug, it's not related to a question!
  • Alexander Farber
    Alexander Farber almost 6 years
    Glide is 5x more code than Picasso. When already using okhttp, better to use Picasso
  • Orri
    Orri over 5 years
    Both libraries use caching but cache differently: medium.com/@multidots/glide-vs-picasso-930eed42b81d
  • A1m
    A1m about 5 years
    This solution (Edit #2) works for me, but I am kinda confused how this is the proper solution to this problem. Is this really what Picasso suggests you to do when you want to have full offline support?
  • Arpit Anand
    Arpit Anand over 3 years
    Picasso's version has changed and needed an update. I have suggested an edit! Great answer though. It worked for me after doing some changing.
  • Abhinav Chauhan
    Abhinav Chauhan over 3 years
    @ArpitAnand thanks, feel free to improve the answer , I shall accept the changes