127

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?
Sufian
  • 6,405
  • 16
  • 66
  • 120
user93796
  • 18,749
  • 31
  • 94
  • 150

9 Answers9

244

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");
            }
        });
    }
});
Maciej Beimcik
  • 3,218
  • 1
  • 24
  • 26
Sanket Berde
  • 6,555
  • 4
  • 35
  • 39
  • @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 Jun 08 '15 at 03:38
  • @ArtjomB. yes that makes sense. Edited ! – Sanket Berde Jun 10 '15 at 04:28
  • @SanketBerde now i dont need to download images and check to exist in sdcard and show that into such as `ImageView`ok? –  Jul 11 '15 at 11:04
  • 5
    @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? – TheDevMan Aug 31 '15 at 14:41
  • @TheDevMan yes even i noticed the problem.. i will post an edit supporting completely offline operation. – Sanket Berde Sep 02 '15 at 15:27
  • @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. – Sanket Berde Sep 08 '15 at 17:15
  • 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. – Zhang Feb 14 '16 at 05:18
  • @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. – Poras Bhardwaj May 04 '16 at 06:13
  • 1
    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. – zeeshan Jun 17 '16 at 11:24
  • @zeeshan The solution in "EDIT #2" is dirty. According to [doc](http://square.github.io/picasso/2.x/picasso/com/squareup/picasso/NetworkPolicy.html#OFFLINE) 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.) – Weekend Aug 01 '16 at 04:00
  • @Weekend hence the next request with no `NetworkPolicy.OFFLINE` in the `onError()` of the first call. – Sanket Berde Aug 01 '16 at 09:54
  • 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. – Billda Feb 07 '17 at 13:15
  • 1
    With `okhttp3.OkHttpClient` library you have to use `OkHttp3Downloader` class form `compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'` – VKostenc Mar 23 '17 at 14:08
  • java.lang.NoClassDefFoundError: Failed resolution of: Lcom/squareup/okhttp/OkHttpClient; – Ali Ahmed Jun 04 '17 at 05:41
  • With current versions, i give "Failed resolution of: Lcom/squareup/okhttp/OkHttpClient;" exception. But it works great when I use the versions you wrote. – Mete Sep 28 '17 at 13:45
  • @Billda No it will not set cache to infinite. That value will be used to set the size limit for cache. – Prakash Feb 28 '18 at 19:14
  • cache isn't working, I even checked private app folder and `/cache/picasso-cache` folder is empty. I'm trying to cache thumbnails https://stackoverflow.com/a/30045646/4548520 – user25 Jun 05 '18 at 16:31
  • `OkHttp` is already inside picasso why should we put it in gradle? – user924 Jun 28 '18 at 21:26
  • `Picasso.with(getActivity()) .load(imageUrl) .networkPolicy(NetworkPolicy.OFFLINE)` i don't see any picasso cache in app private folder – user924 Jun 28 '18 at 21:33
  • nothing works, I see annoying placeholder all the time, https://github.com/square/picasso/issues/1936 – user924 Jun 28 '18 at 21:41
  • and remove unnecessary lines like `picasso.setIndicatorsEnabled(true); picasso.setLoggingEnabled(true);` it's for debug, it's not related to a question! – user924 Jun 28 '18 at 21:55
  • 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? – A1m Apr 18 '19 at 16:19
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

Community
  • 1
  • 1
ahmed hamdy
  • 5,096
  • 1
  • 47
  • 58
  • 4
    No, this isn't work if app was closed. After app was force stopped all images disappeared. – nbumakov Jan 27 '15 at 13:50
  • 2
    this is giving me this error: `FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient` – CIRCLE Mar 13 '15 at 14:22
  • @CIRCLE sorry for late, To use Example,You need to download first [okhttp] (http://square.github.io/okhttp/) package and [okio] (https://github.com/square/okio) package which used by `okhttp` – ahmed hamdy Mar 26 '15 at 16:11
  • @CIRCLE may be you need to download [okhttp-urlconnection] (http://mvnrepository.com/artifact/com.squareup.okhttp/okhttp-urlconnection) package too – ahmed hamdy Mar 26 '15 at 16:18
  • This is not working for me. Images are being reload each time when I scroll to their position in viewPager – Charu Jun 10 '16 at 06:44
  • @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](http://square.github.io/picasso/) – ahmed hamdy Jun 10 '16 at 15:32
  • it's not available anymore (`.with`), from 2.7 version – user25 Jun 05 '18 at 16:34
  • @user25 Ok, But I didn't use `with` method into my solution – ahmed hamdy Jun 06 '18 at 13:03
  • 1
    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 Jun 28 '18 at 21:49
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);
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Gaurav B
  • 661
  • 8
  • 9
  • 1
    Doesn't work anymore networkInterceptors() returns an immutable list. – noev Jun 29 '16 at 09:36
  • 1
    @noev in OkHttp 3.x you can use builder pattern (see https://github.com/square/okhttp/wiki/Interceptors) to add interceptors. – Gaurav B Jun 29 '16 at 10:52
23

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.

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

tsm
  • 71
  • 1
  • 2
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
       }
Abhinav Chauhan
  • 1,304
  • 1
  • 7
  • 24
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

Qamar
  • 4,959
  • 1
  • 30
  • 49
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...");
                                }

                            });
                }
            });

}
Iman Marashi
  • 5,593
  • 38
  • 51
-2

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

jakub
  • 3,576
  • 3
  • 29
  • 55