Come si usa la cache del disco in Picasso?


119

Sto usando Picasso per visualizzare l'immagine nella mia app 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);
}

Ho abilitato il debug e mostra sempre il rosso e il verde. Ma non mostra mai il giallo

Ora se carico la stessa immagine la prossima volta e Internet non è disponibile, l'immagine non viene caricata.

Domande:

  1. Non ha la cache del disco locale?
  2. Come abilito la cache del disco poiché userò la stessa immagine più volte.
  3. Devo aggiungere un'autorizzazione del disco al file manifest di Android?

Ho lo stesso problema. Non verrà memorizzato nella cache!
Jonathan

Ragazzi, dovreste dare un'occhiata a Fresco lib di Facebook. La sua gestione della cache è fantastica.
Michel Fortes

Risposte:


229

Questo è quello che ho fatto. Funziona bene.

Per prima cosa aggiungi OkHttp al file di build gradle del modulo dell'app:

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'

Quindi crea una classe che si estende 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);

    }
}

aggiungilo al file Manifest come segue:

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

</application>

Ora usa Picasso come faresti normalmente. Nessun cambiamento.

MODIFICARE:

se desideri utilizzare solo immagini memorizzate nella cache. Chiama la biblioteca in questo modo. Ho notato che se non aggiungiamo networkPolicy, le immagini non verranno visualizzate in un avvio completamente offline anche se sono memorizzate nella cache . Il codice seguente risolve il problema.

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

MODIFICA # 2

il problema con il codice sopra è che se svuoti la cache, Picasso continuerà a cercarlo offline nella cache e fallirà, il seguente esempio di codice guarda la cache locale, se non viene trovato offline, va online e riempie la 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");
            }
        });
    }
});

@ArtjomB. , Ho risposto alla domanda. E la soluzione funziona. Ma c'è questa piccola precisazione che posso usare. Ho esaminato la documentazione di OkHttp e non menzionano l'unità di "cache". Quindi, se qualcuno volesse condividere un po 'di saggezza ... questa è una buona opportunità.
Sanket Berde

@ArtjomB. sì, ha senso. Modificato !
Sanket Berde

5
@SanketBerde: Grazie per la breve nota, ma ho capito che l'immagine viene fuori dalla memoria solo se l'app è in esecuzione in background (quando offline). se chiudo l'app, è chiaro che le app in esecuzione, quindi riapro la mia app le immagini non vengono caricate dalla cache. Ho impostato l'errore di caricamento predefinito dell'immagine in arrivo. Cosa potrebbe essere sbagliato qui?
TheDevMan

1
Forse Picasso ha cambiato il modo in cui funzionano le cose, perché per me funziona bene senza okhttp e criteri di rete. Al nuovo inizio, ottiene immediatamente le immagini dal disco e quando la rete è offline, le mostra comunque bene.
zeeshan

1
Con la okhttp3.OkHttpClientlibreria devi usare il OkHttp3Downloadermodulo di classe compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck

46

1) risposta alla prima domanda: secondo il metodo Picasso Doc for With ()

L'istanza Picasso predefinita globale restituita da with () viene inizializzata automaticamente con i valori predefiniti adatti alla maggior parte delle implementazioni.

  • Memoria cache LRU pari al 15% della RAM disponibile per l'applicazione
  • Cache su disco del 2% di spazio di archiviazione fino a 50 MB ma non meno di 5 MB.

Ma il Disk Cache funzionamento per il Default Picasso globale è disponibile solo su API 14+

2) risposta della seconda domanda: Picassousa la HTTPrichiesta del client per l' Disk Cacheoperazione Quindi puoi creare la tua proprietà http request headerhas Cache-Controlcon max-age E creare la tua istanza Picasso statica invece di Picasso predefinita Usando

1] HttpResponseCache (Nota: funziona solo per API 13+)
2] OkHttpClient (funziona per tutte le API)

Esempio per l'utilizzo OkHttpClientper creare la tua classe Picasso statica:

  • Per prima cosa crea una nuova classe per ottenere il tuo picassooggetto 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;
        }
    
    } 
  • usa il tuo picassooggetto singleton invece diPicasso.With()

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

3) risposta per la terza domanda: non è necessario alcun permesso del disco per le operazioni di cache del disco

Riferimenti : problema di Github relativo alla cache del disco , @ jake-wharton ha risposto a due domande -> Question1 e Question2


4
No, non funziona se l'app è stata chiusa. Dopo che l'app è stata interrotta forzatamente, tutte le immagini sono scomparse.
nbumakov

2
questo mi dà questo errore:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CERCHIO

@CIRCLE scusa per il ritardo, per usare l'esempio, devi scaricare prima il pacchetto [okhttp] ( square.github.io/okhttp ) e il pacchetto [okio] ( github.com/square/okio ) utilizzato daokhttp
ahmed hamdy

@CIRCLE potrebbe essere necessario scaricare anche il pacchetto [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… )
ahmed hamdy

Questo non funziona per me. Le immagini vengono ricaricate ogni volta che scorro fino alla loro posizione in viewPager
Charu

21

Per la memorizzazione nella cache, utilizzerei gli intercettori OkHttp per ottenere il controllo sulla politica di memorizzazione nella cache. Dai un'occhiata a questo esempio incluso nella libreria OkHttp.

RewriteResponseCacheControl.java

Ecco come lo userei con 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);

1
Non funziona più networkInterceptors () restituisce un elenco immutabile.
noev

1
@noev in OkHttp 3.x puoi usare il pattern builder (vedi github.com/square/okhttp/wiki/Interceptors ) per aggiungere intercettori.
Gaurav B

10

Per la versione più aggiornata 2.71828 Queste sono le tue risposte.

Q1 : non ha la cache del disco locale?

A1 : C'è un caching predefinito all'interno di Picasso e il flusso di richieste proprio come questo

App -> Memory -> Disk -> Server

Ovunque abbiano incontrato prima la loro immagine, useranno quell'immagine e poi interromperanno il flusso di richieste. E il flusso di risposta? Non preoccuparti, eccolo qui.

Server -> Disk -> Memory -> App

Per impostazione predefinita, verranno archiviati prima in un disco locale per la cache di conservazione estesa. Quindi la memoria, per l'utilizzo dell'istanza della cache.

È possibile utilizzare l'indicatore integrato in Picasso per vedere dove si formano le immagini abilitandolo.

Picasso.get().setIndicatorEnabled(true);

Apparirà una bandiera nell'angolo in alto a sinistra delle tue immagini.

  • La bandiera rossa indica che le immagini provengono dal server. (Nessuna memorizzazione nella cache al primo caricamento)
  • Bandiera blu significa che le foto provengono dal disco locale. (Caching)
  • La bandiera verde significa che le immagini provengono dalla memoria. (Memorizzazione nella cache delle istanze)

D2 : Come si abilita la memorizzazione nella cache del disco poiché userò la stessa immagine più volte?

A2 : non è necessario abilitarlo. È l'impostazione predefinita.

Quello che devi fare è DISATTIVARLO quando vuoi che le tue immagini siano sempre fresche. Sono disponibili due modalità di memorizzazione nella cache disabilitata.

  1. Impostare .memoryPolicy()su NO_CACHE e / o NO_STORE e il flusso sarà simile a questo.

NO_CACHE salterà la ricerca delle immagini dalla memoria.

App -> Disk -> Server

NO_STORE salterà le immagini di archiviazione in memoria al primo caricamento delle immagini.

Server -> Disk -> App
  1. Impostare .networkPolicy()su NO_CACHE e / o NO_STORE e il flusso sarà simile a questo.

NO_CACHE salterà la ricerca di immagini dal disco.

App -> Memory -> Server

NO_STORE salterà le immagini di memorizzazione nel disco quando le prime immagini caricate.

Server -> Memory -> App

Non è possibile DISABILITARE né completamente nessuna immagine memorizzata nella cache. Ecco un esempio.

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

Il flusso di nessuna memorizzazione nella cache e nessuna memorizzazione sarà simile a questo.

App -> Server //Request

Server -> App //Response

Quindi, potrebbe essere necessario anche per ridurre al minimo l'utilizzo dello spazio di archiviazione dell'app.

D3 : Devo aggiungere alcune autorizzazioni del disco al file manifest di Android?

A3 : No, ma non dimenticare di aggiungere l'autorizzazione INTERNET per la tua richiesta HTTP.


6

1) Picasso per impostazione predefinita ha una cache (vedi risposta ahmed hamdy)

2) Se proprio devi prendere l'immagine dalla cache del disco e poi dalla rete, ti consiglio di scrivere il tuo 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;
        }

    }
}

E in Application singleton nel metodo OnCreate usalo con 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) Non sono necessarie autorizzazioni per il defalut della cartella della cache dell'applicazione


1

Aggiungi il codice seguente e usalo Application.onCreatenormalmente

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

Se prima memorizzi nella cache le immagini, fai qualcosa di simile 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);
        }

e carica le tue immagini come al solito o con la cache del disco

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

Nota:

Il colore rosso indica che l'immagine è stata recuperata dalla rete .

Il colore verde indica che l'immagine è stata recuperata dalla memoria cache .

Il colore blu indica che l'immagine è stata recuperata dalla memoria del disco .

Prima di rilasciare l'app, eliminala o impostala false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);se non richiesto. thankx


1

Non so quanto sia buona questa soluzione ma è sicuramente LA FACILE che ho appena usato nella mia app e funziona bene

carichi l'immagine in questo modo

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

Puoi ottenere bimapcosì

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

Ora coprilo Bitmapin un JPGfile e memorizzalo nella cache, di seguito è riportato il codice completo per ottenere il bimap e memorizzarlo nella 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();
                    })

il get()metodo Piccassoper qualche motivo doveva essere chiamato su un thread separato, sto salvando quell'immagine anche sullo stesso thread.

Una volta salvata l'immagine, puoi ottenere tutti i file in questo modo

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

ora puoi trovare il file che stai cercando come di seguito

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
       }

0

Uso questo codice e ha funzionato, forse utile per te:

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...");
                                }

                            });
                }
            });

}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.