How do I use disk caching in Picasso?
Asked Answered
H

9

129

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?
Heterothallic answered 1/6, 2014 at 10:19 Comment(2)
I'm having same issue. It won't cache!Poky
Guys, you should take a look at facebook´s Fresco lib. Its cache management is awesome.Hutcheson
N
245

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");
            }
        });
    }
});
Neocolonialism answered 6/6, 2015 at 19:56 Comment(21)
@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.Neocolonialism
@SanketBerde now i dont need to download images and check to exist in sdcard and show that into such as ImageViewok?Heavyladen
@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?Semivitreous
@Semivitreous yes even i noticed the problem.. i will post an edit supporting completely offline operation.Neocolonialism
@Semivitreous 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.Neocolonialism
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.Apron
@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.Acknowledgment
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.Midship
@Midship 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.)Featly
@Featly hence the next request with no NetworkPolicy.OFFLINE in the onError() of the first call.Neocolonialism
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.Karolinekaroly
With okhttp3.OkHttpClient library you have to use OkHttp3Downloader class form compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'Moultrie
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/squareup/okhttp/OkHttpClient;Eclampsia
With current versions, i give "Failed resolution of: Lcom/squareup/okhttp/OkHttpClient;" exception. But it works great when I use the versions you wrote.Looby
@Karolinekaroly No it will not set cache to infinite. That value will be used to set the size limit for cache.Maxine
cache isn't working, I even checked private app folder and /cache/picasso-cache folder is empty. I'm trying to cache thumbnails https://mcmap.net/q/175452/-how-to-load-video-thumbnails-using-square-picasso-libraryEadie
OkHttp is already inside picasso why should we put it in gradle?Goblin
Picasso.with(getActivity()) .load(imageUrl) .networkPolicy(NetworkPolicy.OFFLINE) i don't see any picasso cache in app private folderGoblin
nothing works, I see annoying placeholder all the time, github.com/square/picasso/issues/1936Goblin
and remove unnecessary lines like picasso.setIndicatorsEnabled(true); picasso.setLoggingEnabled(true); it's for debug, it's not related to a question!Goblin
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?Serrated
B
47

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

Baud answered 11/12, 2014 at 11:25 Comment(9)
No, this isn't work if app was closed. After app was force stopped all images disappeared.Briefcase
this is giving me this error: FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClientGloze
@Gloze 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 okhttpBaud
@Gloze may be you need to download [okhttp-urlconnection] (mvnrepository.com/artifact/com.squareup.okhttp/…) package tooBaud
This is not working for me. Images are being reload each time when I scroll to their position in viewPagerUnseal
@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 docBaud
it's not available anymore (.with), from 2.7 versionEadie
@Eadie Ok, But I didn't use with method into my solutionBaud
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)Goblin
T
24

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().setIndicatorsEnabled(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.

Tombola answered 19/7, 2019 at 14:51 Comment(0)
T
23

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);
Tongue answered 20/10, 2015 at 17:10 Comment(2)
Doesn't work anymore networkInterceptors() returns an immutable list.Midvictorian
@Midvictorian in OkHttp 3.x you can use builder pattern (see github.com/square/okhttp/wiki/Interceptors) to add interceptors.Tongue
N
7

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

Neddra answered 31/8, 2016 at 13:45 Comment(0)
C
2

I don't know how good that solution is but it is definitely THE EASY ONE i just used in my app and it is working fine

you load the image like that

public void loadImage (){
Picasso picasso = Picasso.get(); 
picasso.setIndicatorsEnabled(true);
picasso.load(quiz.getImageUrl()).into(quizImage);
}

You can get the bimap like that

Bitmap bitmap = Picasso.get().load(quiz.getImageUrl()).get();

Now covert that Bitmap into a JPG file and store in the in the cache, below is complete code for getting the bimap and caching it

Thread thread = new Thread() {
 public void run() {
 File file = new File(getCacheDir() + "/" +member.getMemberId() + ".jpg");

try {
      Bitmap bitmap = Picasso.get().load(uri).get();
      FileOutputStream fOut = new FileOutputStream(file);                                        
      bitmap.compress(Bitmap.CompressFormat.JPEG, 100,new FileOutputStream(file));
fOut.flush();
fOut.close();
    }
catch (Exception e) {
  e.printStackTrace();
    }
   }
};
     thread.start();
  })

the get() method of Piccasso need to be called on separate thread , i am saving that image also on that same thread.

Once the image is saved you can get all the files like that

List<File> files = new LinkedList<>(Arrays.asList(context.getExternalCacheDir().listFiles()));

now you can find the file you are looking for like below

for(File file : files){
                if(file.getName().equals("fileyouarelookingfor" + ".jpg")){ // you need the name of the file, for example you are storing user image and the his image name is same as his id , you can call getId() on user to get the file name
                    Picasso.get() // if file found then load it
                            .load(file)
                            .into(mThumbnailImage);
                    return; // return 
                }
        // fetch it over the internet here because the file is not found
       }
Chiefly answered 8/5, 2020 at 12:29 Comment(2)
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.Simba
@ArpitAnand thanks, feel free to improve the answer , I shall accept the changesChiefly
A
1

Add followning code in Application.onCreate then use it normal

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

If you cache images first then do something like this in ProductImageDownloader.doBackground

final Callback callback = new Callback() {
            @Override
            public void onSuccess() {
                downLatch.countDown();
                updateProgress();
            }

            @Override
            public void onError() {
                errorCount++;
                downLatch.countDown();
                updateProgress();
            }
        };
        Picasso.with(context).load(Constants.imagesUrl+productModel.getGalleryImage())
                .memoryPolicy(MemoryPolicy.NO_CACHE).fetch(callback);
        Picasso.with(context).load(Constants.imagesUrl+productModel.getLeftImage())
                .memoryPolicy(MemoryPolicy.NO_CACHE).fetch(callback);
        Picasso.with(context).load(Constants.imagesUrl+productModel.getRightImage())
                .memoryPolicy(MemoryPolicy.NO_CACHE).fetch(callback);

        try {
            downLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if(errorCount == 0){
            products.remove(productModel);
            productModel.isDownloaded = true;
            productsDatasource.updateElseInsert(productModel);
        }else {
            //error occurred while downloading images for this product
            //ignore error for now
            // FIXME: 9/27/2017 handle error
            products.remove(productModel);

        }
        errorCount = 0;
        downLatch = new CountDownLatch(3);

        if(!products.isEmpty() /*&& testCount++ < 30*/){
            startDownloading(products.get(0));
        }else {
            //all products with images are downloaded
            publishProgress(100);
        }

and load your images like normal or with disk caching

    Picasso.with(this).load(Constants.imagesUrl+batterProduct.getGalleryImage())
        .networkPolicy(NetworkPolicy.OFFLINE)
        .placeholder(R.drawable.GalleryDefaultImage)
        .error(R.drawable.GalleryDefaultImage)
        .into(viewGallery);

Note:

Red color indicates that image is fetched from network.

Green color indicates that image is fetched from cache memory.

Blue color indicates that image is fetched from disk memory.

Before releasing the app delete or set it false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true); if not required. Thankx

Aboutface answered 29/9, 2017 at 8:18 Comment(0)
B
0

I use this code and worked, maybe useful for you:

public static void makeImageRequest(final View parentView,final int id, final String imageUrl) {

    final int defaultImageResId = R.mipmap.user;
    final ImageView imageView = (ImageView) parentView.findViewById(id);
    Picasso.with(context)
            .load(imageUrl)
            .networkPolicy(NetworkPolicy.OFFLINE)
            .into(imageView, new Callback() {
                @Override
                public void onSuccess() {
                Log.v("Picasso","fetch image success in first time.");
                }

                @Override
                public void onError() {
                    //Try again online if cache failed
                    Log.v("Picasso","Could not fetch image in first time...");
                    Picasso.with(context).load(imageUrl).networkPolicy(NetworkPolicy.NO_CACHE)
                            .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE).error(defaultImageResId)
                            .into(imageView, new Callback() {

                                @Override
                                public void onSuccess() {
                                    Log.v("Picasso","fetch image success in try again.");
                                }

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

                            });
                }
            });

}
Buccaneer answered 20/11, 2016 at 21:37 Comment(0)
D
-2

I had the same problem and used Glide library instead. Cache is out of the box there. https://github.com/bumptech/glide

Debor answered 5/5, 2018 at 15:48 Comment(2)
Glide is 5x more code than Picasso. When already using okhttp, better to use PicassoUnionist
Both libraries use caching but cache differently: medium.com/@multidots/glide-vs-picasso-930eed42b81dJotter

© 2022 - 2024 — McMap. All rights reserved.