Comment utiliser la mise en cache de disque dans Picasso?

119

J'utilise Picasso pour afficher l'image dans mon application Android:

/**
* 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);
}

J'ai activé le débogage et il montre toujours le rouge et le vert. Mais jamais le jaune

Maintenant, si je charge la même image la prochaine fois et qu'Internet n'est pas disponible, l'image n'est pas chargée.

Des questions:

  1. N'a-t-il pas de cache disque local?
  2. Comment activer la mise en cache du disque car j'utiliserai la même image plusieurs fois.
  3. Dois-je ajouter une autorisation de disque au fichier manifeste Android?
user93796
la source
J'ai le même problème. Il ne cache pas!
Jonathan
Les gars, vous devriez jeter un oeil à la librairie Fresco de facebook. Sa gestion du cache est géniale.
Michel Fortes

Réponses:

229

C'est ce que j'ai fait. Fonctionne bien.

Ajoutez d'abord OkHttp au fichier de construction gradle du module d'application:

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'

Ensuite, faites une classe en extension 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);

    }
}

ajoutez-le au fichier Manifest comme suit:

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

</application>

Utilisez maintenant Picasso comme vous le feriez normalement. Aucun changement.

ÉDITER:

si vous souhaitez utiliser uniquement des images mises en cache. Appelez la bibliothèque comme ça. J'ai remarqué que si nous n'ajoutons pas le networkPolicy, les images n'apparaîtront pas dans un démarrage entièrement hors ligne, même si elles sont mises en cache . Le code ci-dessous résout le problème.

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

MODIFIER # 2

le problème avec le code ci-dessus est que si vous effacez le cache, Picasso continuera à le rechercher hors ligne dans le cache et échouera, l'exemple de code suivant examine le cache local, s'il n'est pas trouvé hors ligne, il se met en ligne et reconstitue le 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");
            }
        });
    }
});
Sanket Berde
la source
@ArtjomB. , J'ai répondu à la question. Et la solution fonctionne. Mais il y a cette petite clarification que je peux utiliser. J'ai parcouru la documentation d'OkHttp et ils ne mentionnent pas l'unité de "cache". Donc, si quelqu'un souhaite partager une certaine sagesse ... c'est une bonne opportunité.
Sanket Berde
@ArtjomB. oui cela a du sens. Édité!
Sanket Berde
5
@SanketBerde: Merci pour la note rapide, mais j'ai compris que l'image ne sortait de la mémoire que si l'application s'exécutait en arrière-plan (lorsqu'elle est hors ligne). si je ferme l'application, c'est effacer les applications en cours d'exécution, puis rouvrir mon application, les images ne se chargent pas à partir du cache. J'ai défini l'erreur de chargement par défaut de l'image qui arrive. Qu'est-ce qui ne va pas ici?
TheDevMan
1
Peut-être que Picasso a changé la façon dont les choses fonctionnent, car pour moi, cela fonctionne bien sans okhttp et politique réseau. Lors d'un nouveau départ, il obtient instantanément des images du disque et lorsque le réseau est hors ligne, il les affiche toujours correctement.
zeeshan
1
Avec la okhttp3.OkHttpClientbibliothèque, vous devez utiliser le OkHttp3Downloaderformulaire de classe compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck
46

1) réponse à la première question: selon Picasso Doc pour la méthode With ()

L'instance globale de Picasso par défaut renvoyée par with () est automatiquement initialisée avec des valeurs par défaut adaptées à la plupart des implémentations.

  • Cache mémoire LRU de 15% de la RAM d'application disponible
  • Cache disque de 2% d'espace de stockage jusqu'à 50 Mo mais pas moins de 5 Mo.

Mais l' Disk Cache opération pour Picasso global par défaut n'est disponible que sur l'API 14+

2) réponse à la deuxième question: Picassoutilisez la HTTPdemande du client pour l' Disk Cacheopération afin que vous puissiez créer votre propre http request headerpropriété a Cache-Controlavec max-age Et créer votre propre instance statique de Picasso au lieu de Picasso par défaut En utilisant

1] HttpResponseCache (Remarque: fonctionne uniquement pour l'API 13+)
2] OkHttpClient (fonctionne pour toutes les API)

Exemple d'utilisation OkHttpClientpour créer votre propre classe statique Picasso:

  • Créez d'abord une nouvelle classe pour obtenir votre propre picassoobjet singleton

    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;
        }
    
    } 
  • utilisez votre propre picassoobjet singleton au lieu dePicasso.With()

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

3) réponse à la troisième question: vous n'avez besoin d'aucune autorisation de disque pour les opérations de cache de disque

Références : problème Github sur le cache disque , deux questions ont été répondues par @ jake-wharton -> Question1 et Question2

ahmed hamdy
la source
4
Non, cela ne fonctionne pas si l'application a été fermée. Une fois l'application arrêtée, toutes les images ont disparu.
nbumakov
2
cela me donne cette erreur:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCLE
@CIRCLE désolé pour la fin, pour utiliser l'exemple, vous devez d'abord télécharger le package [okhttp] ( square.github.io/okhttp ) et le package [okio] ( github.com/square/okio ) utilisé parokhttp
ahmed hamdy
@CIRCLE peut-être que vous devez télécharger le paquet [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… ) aussi
ahmed hamdy
Cela ne fonctionne pas pour moi. Les images sont rechargées à chaque fois que je fais défiler jusqu'à leur position dans la vuePager
Charu
21

Pour la mise en cache, j'utiliserais des intercepteurs OkHttp pour prendre le contrôle de la politique de mise en cache. Consultez cet exemple inclus dans la bibliothèque OkHttp.

RewriteResponseCacheControl.java

Voici comment je l'utiliserais avec 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);
Gaurav B
la source
1
Ne fonctionne plus networkInterceptors () renvoie une liste immuable.
noev
1
@noev dans OkHttp 3.x, vous pouvez utiliser un modèle de générateur (voir github.com/square/okhttp/wiki/Interceptors ) pour ajouter des intercepteurs.
Gaurav B le
10

Pour la version 2.71828 la plus récente, voici votre réponse.

Q1 : N'a-t-il pas de cache disque local?

A1 : Il y a une mise en cache par défaut dans Picasso et le flux de demande comme celui-ci

App -> Memory -> Disk -> Server

Partout où ils ont rencontré leur image en premier, ils utiliseront cette image, puis arrêteront le flux de demandes. Qu'en est-il du flux de réponse? Ne vous inquiétez pas, c'est ici.

Server -> Disk -> Memory -> App

Par défaut, ils seront d'abord stockés sur un disque local pour le cache de conservation étendu. Ensuite, la mémoire, pour l'utilisation de l'instance du cache.

Vous pouvez utiliser l'indicateur intégré dans Picasso pour voir où se forment les images en activant cette option.

Picasso.get().setIndicatorEnabled(true);

Il affichera un drapeau dans le coin supérieur gauche de vos photos.

  • Le drapeau rouge signifie que les images proviennent du serveur. (Pas de mise en cache au premier chargement)
  • Le drapeau bleu signifie que les photos proviennent du disque local. (Mise en cache)
  • Le drapeau vert signifie que les images proviennent de la mémoire. (Mise en cache d'instance)

Q2 : Comment activer la mise en cache du disque car j'utiliserai la même image plusieurs fois?

A2 : Vous n'êtes pas obligé de l'activer. C'est la valeur par défaut.

Ce que vous devrez faire est de le désactiver lorsque vous voulez que vos images soient toujours fraîches. Il existe deux méthodes de mise en cache désactivée.

  1. Réglez .memoryPolicy()sur NO_CACHE et / ou NO_STORE et le flux ressemblera à ceci.

NO_CACHE sautera la recherche d'images de la mémoire.

App -> Disk -> Server

NO_STORE ignorera le stockage des images en mémoire lors du premier chargement d'images.

Server -> Disk -> App
  1. Réglez .networkPolicy()sur NO_CACHE et / ou NO_STORE et le flux ressemblera à ceci.

NO_CACHE sautera la recherche d'images à partir du disque.

App -> Memory -> Server

NO_STORE ignorera le stockage des images sur le disque lors du premier chargement d'images.

Server -> Memory -> App

Vous ne pouvez désactiver ni l'un ni l'autre pour aucune mise en cache des images. Voici un exemple.

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

Le flux sans mise en cache ni stockage ressemblera à ceci.

App -> Server //Request

Server -> App //Response

Vous en aurez peut-être besoin pour réduire également l'utilisation du stockage de votre application.

Q3 : Dois-je ajouter une autorisation de disque au fichier manifeste Android?

A3 : Non, mais n'oubliez pas d'ajouter l'autorisation INTERNET pour votre requête HTTP.

Narerit Rittiplaeng
la source
6

1) Picasso par défaut a un cache (voir la réponse d'Ahmed Hamdy)

2) Si vous devez vraiment prendre une image du cache disque puis du réseau, je vous recommande d'écrire votre propre téléchargeur:

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;
        }

    }
}

Et dans Application singleton dans la méthode OnCreate, utilisez-le avec 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) Aucune autorisation requise pour le dossier de cache d'application par défaut

tsm
la source
1

Ajoutez le code suivant Application.onCreatepuis utilisez-le normalement

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

Si vous mettez d'abord les images en cache, faites quelque chose comme ça dans 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);
        }

et chargez vos images comme d'habitude ou avec la mise en cache disque

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

Remarque:

La couleur rouge indique que l'image est extraite du réseau .

La couleur verte indique que l'image est extraite de la mémoire cache .

La couleur bleue indique que l'image est extraite de la mémoire du disque .

Avant de publier l'application, supprimez-la ou définissez-la false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);si cela n'est pas nécessaire. Merci

Qamar
la source
1

Je ne sais pas à quel point cette solution est bonne mais c'est définitivement LA FACILE je viens d'utiliser dans mon application et elle fonctionne bien

vous chargez l'image comme ça

public void loadImage (){
Picasso picasso = Picasso.with(this); 
picasso.setDebugging(true);
picasso.load(quiz.getImageUrl()).into(quizImage);
}

Vous pouvez obtenir le bimapcomme ça

Bitmap bitmap = Piccaso.with(this).load(quiz.getImageUrl()).get();

Maintenant, dissimulez cela Bitmapdans un JPGfichier et stockez-le dans le cache, ci-dessous le code complet pour obtenir la bimap et la mettre en cache

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

                                try {
                                    Bitmap bitmap = Picasso.with(getActivity())
                                            .load(uri).get();
                                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100,new FileOutputStream(file));

                                } catch (Exception e) {
                                   e.printStackTrace();
                                }
                            }
                        };
                        thread.start();
                    })

la get()méthode de Piccassopour une raison quelconque devait être appelée sur un fil séparé, j'enregistre également cette image sur ce même fil.

Une fois l'image enregistrée, vous pouvez obtenir tous les fichiers comme ça

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

maintenant vous pouvez trouver le fichier que vous recherchez comme ci-dessous

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.with(getActivity()) // 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
la source
0

J'utilise ce code et j'ai travaillé, peut-être utile pour vous:

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
la source