Wie verwende ich das Festplatten-Caching in Picasso?


119

Ich verwende Picasso, um Bilder in meiner Android-App anzuzeigen:

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

Ich habe das Debuggen aktiviert und es wird immer entweder rot oder grün angezeigt. Es wird jedoch nie gelb angezeigt

Wenn ich jetzt das nächste Mal dasselbe Bild lade und das Internet nicht verfügbar ist, wird das Bild nicht geladen.

Fragen:

  1. Hat es keinen lokalen Festplatten-Cache?
  2. Wie aktiviere ich das Festplatten-Caching, da ich dasselbe Image mehrmals verwenden werde?
  3. Muss ich der Android-Manifestdatei eine Festplattenberechtigung hinzufügen?

Ich habe das gleiche Problem. Es wird nicht zwischengespeichert!
Jonathan

Leute, ihr solltet euch die Fresco lib von Facebook ansehen. Das Cache-Management ist fantastisch.
Michel Fortes

Antworten:


229

Das habe ich getan. Funktioniert gut.

Fügen Sie zuerst das OkHttp zur Gradle-Build-Datei des App-Moduls hinzu:

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'

Dann machen Sie eine Klasse zu erweitern 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);

    }
}

Fügen Sie es wie folgt zur Manifest-Datei hinzu:

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

</application>

Verwenden Sie jetzt Picasso wie gewohnt. Keine Änderungen.

BEARBEITEN:

Wenn Sie nur zwischengespeicherte Bilder verwenden möchten. Rufen Sie die Bibliothek so auf. Ich habe festgestellt, dass Bilder nicht vollständig offline angezeigt werden, wenn wir die networkPolicy nicht hinzufügen, selbst wenn sie zwischengespeichert werden . Der folgende Code löst das Problem.

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

EDIT # 2

Das Problem mit dem obigen Code besteht darin, dass Picasso beim Löschen des Caches weiterhin offline im Cache sucht und fehlschlägt. Das folgende Codebeispiel zeigt den lokalen Cache. Wenn er nicht offline gefunden wird, wird er online geschaltet und füllt den Cache auf.

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. Ich habe die Frage beantwortet. Und die Lösung funktioniert. Aber es gibt diese kleine Klarstellung, die ich verwenden kann. Ich habe die OkHttp-Dokumentation durchgesehen und sie erwähnen nicht die Einheit "Cache". Wenn also jemand etwas Weisheit teilen möchte, ist dies eine gute Gelegenheit.
Sanket Berde

@ArtjomB. ja das macht sinn Bearbeitet!
Sanket Berde

5
@SanketBerde: Danke für den kurzen Hinweis, aber ich habe herausgefunden, dass das Bild nur dann aus dem Speicher kommt, wenn die App im Hintergrund ausgeführt wird (wenn sie offline ist). Wenn ich die App schließe, ist klar, dass die laufenden Apps ausgeführt werden. Öffnen Sie dann meine App erneut. Die Bilder werden nicht aus dem Cache geladen. Ich habe den Standardfehler beim Laden des Bildes festgelegt, der angezeigt wird. Was könnte hier falsch sein?
TheDevMan

1
Vielleicht hat Picasso die Funktionsweise geändert, weil es für mich ohne OKHTTP und Netzwerkrichtlinien gut funktioniert. Bei einem Neustart werden sofort Bilder von der Festplatte abgerufen, und wenn das Netzwerk offline ist, werden sie weiterhin in Ordnung angezeigt.
Zeeshan

1
Mit okhttp3.OkHttpClientBibliothek müssen Sie OkHttp3DownloaderKlassenform verwenden compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck

46

1) Antwort auf die erste Frage: gemäß Picasso Doc for With () -Methode

Die globale Standard-Picasso-Instanz, die von with () zurückgegeben wird, wird automatisch mit Standardeinstellungen initialisiert, die für die meisten Implementierungen geeignet sind.

  • LRU-Speichercache von 15% des verfügbaren Anwendungs-RAM
  • Festplatten-Cache mit 2% Speicherplatz bis zu 50 MB, jedoch nicht weniger als 5 MB.

Der Disk Cache Betrieb für Global Default Picasso ist jedoch nur für API 14+ verfügbar

2) Antwort der zweiten Frage: PicassoDie Verwendung HTTPClient - Anfrage an den Disk CacheBetrieb So können Sie Ihre eigene machen http request headerhat die Eigenschaft Cache-Controlmitmax-age und erstellen Sie Ihre eigene statische Picasso Instanz statt Standard Picasso Durch die Verwendung von

1] HttpResponseCache (Hinweis: Funktioniert nur für API 13+)
2] OkHttpClient (Funktioniert für alle APIs)

Beispiel für die OkHttpClientErstellung einer eigenen statischen Picasso-Klasse:

  • Erstellen Sie zuerst eine neue Klasse, um Ihr eigenes Singleton- picassoObjekt zu erhalten

    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;
        }
    
    } 
  • Verwenden Sie picassostattdessen Ihr eigenes Singleton- ObjektPicasso.With()

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

3) Antwort auf die dritte Frage: Sie benötigen keine Festplattenberechtigungen für Festplatten-Cache-Vorgänge

Referenzen : Github-Problem mit dem Festplatten-Cache , zwei Fragen wurden von @ jake-wharton beantwortet -> Frage1 und Frage2


4
Nein, das funktioniert nicht, wenn die App geschlossen wurde. Nachdem die App gestoppt wurde, verschwanden alle Bilder.
Nbumakov

2
Dies gibt mir diesen Fehler:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCLE

@CIRCLE Entschuldigung für die Verspätung. Um das Beispiel zu verwenden, müssen Sie zuerst das Paket [okhttp] ( square.github.io/okhttp ) und das Paket [okio] ( github.com/square/okio ) herunterladen , das vonokhttp
ahmed hamdy am

@CIRCLE Möglicherweise müssen Sie auch das Paket [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… ) herunterladen
ahmed hamdy

Das funktioniert bei mir nicht. Bilder werden jedes Mal neu geladen, wenn ich zu ihrer Position in viewPager
Charu

21

Für das Caching würde ich OkHttp- Interceptors verwenden , um die Kontrolle über die Caching-Richtlinie zu erlangen. Schauen Sie sich dieses Beispiel an, das in der OkHttp-Bibliothek enthalten ist.

RewriteResponseCacheControl.java

So würde ich es mit Picasso verwenden -

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
Funktioniert nicht mehr networkInterceptors () gibt eine unveränderliche Liste zurück.
Noev

1
@noev In OkHttp 3.x können Sie das Builder-Muster (siehe github.com/square/okhttp/wiki/Interceptors ) verwenden, um Interceptors hinzuzufügen.
Gaurav B

10

Für die aktuellste Version 2.71828 Dies sind Ihre Antworten.

Q1 : Hat es keinen lokalen Festplatten-Cache?

A1 : In Picasso gibt es Standard-Caching, und der Anforderungsfluss ist genau so

App -> Memory -> Disk -> Server

Wo immer sie zuerst ihr Bild getroffen haben, verwenden sie dieses Bild und stoppen dann den Anforderungsfluss. Was ist mit dem Antwortfluss? Mach dir keine Sorgen, hier ist es.

Server -> Disk -> Memory -> App

Standardmäßig werden sie zuerst auf einer lokalen Festplatte für den erweiterten Aufbewahrungscache gespeichert. Dann der Speicher für die Instanznutzung des Caches.

Sie können die in Picasso integrierte Anzeige verwenden, um zu sehen, wo sich Bilder bilden, indem Sie diese Option aktivieren.

Picasso.get().setIndicatorEnabled(true);

In der oberen linken Ecke Ihrer Bilder wird eine Flagge angezeigt.

  • Rote Flagge bedeutet, dass die Bilder vom Server stammen. (Kein Caching beim ersten Laden)
  • Blaue Flagge bedeutet, dass die Fotos von der lokalen Festplatte stammen. (Caching)
  • Grüne Flagge bedeutet, dass die Bilder aus dem Speicher stammen. (Instanz-Caching)

F2 : Wie aktiviere ich das Festplatten-Caching, da ich dasselbe Image mehrmals verwenden werde?

A2 : Sie müssen es nicht aktivieren. Es ist die Standardeinstellung.

Sie müssen es deaktivieren , wenn Ihre Bilder immer frisch sein sollen. Es gibt zwei Möglichkeiten zum Deaktivieren des Caching.

  1. Setzen Sie .memoryPolicy()auf no_cache und / oder NO_STORE und der Fluss wird so aussehen.

NO_CACHE überspringt das Nachschlagen von Bildern aus dem Speicher.

App -> Disk -> Server

NO_STORE überspringt beim ersten Laden von Bildern das Speichern von Bildern im Speicher.

Server -> Disk -> App
  1. Setzen Sie .networkPolicy()auf no_cache und / oder NO_STORE und der Fluss wird so aussehen.

NO_CACHE überspringt das Nachschlagen von Bildern von der Festplatte.

App -> Memory -> Server

NO_STORE überspringt beim ersten Laden von Bildern das Speichern von Bildern auf der Festplatte.

Server -> Memory -> App

Sie können weder deaktivieren, um Bilder vollständig zwischenzuspeichern. Hier ist ein Beispiel.

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

Der Fluss ohne Caching und ohne Speicherung wird so aussehen.

App -> Server //Request

Server -> App //Response

Möglicherweise benötigen Sie dies, um auch die Nutzung Ihres App-Speichers zu minimieren.

Q3 : Muss ich der Android-Manifestdatei eine Festplattenberechtigung hinzufügen?

A3 : Nein, aber vergessen Sie nicht, die INTERNET-Berechtigung für Ihre HTTP-Anforderung hinzuzufügen.


6

1) Picasso hat standardmäßig einen Cache (siehe ahmed hamdy Antwort)

2) Wenn Sie wirklich ein Image aus dem Festplatten-Cache und dann aus dem Netzwerk nehmen müssen, empfehle ich, einen eigenen Downloader zu schreiben:

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

    }
}

Und in Application Singleton in der Methode OnCreate verwenden Sie es mit 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) Für den Standard-Anwendungscache-Ordner sind keine Berechtigungen erforderlich


1

Fügen Sie folgenden Code hinzu Application.onCreateund verwenden Sie ihn dann normal

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

Wenn Sie zuerst Bilder zwischenspeichern, machen Sie so etwas 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);
        }

und laden Sie Ihre Bilder wie gewohnt oder mit Festplatten-Caching

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

Hinweis:

Die rote Farbe zeigt an, dass das Bild aus dem Netzwerk abgerufen wird .

Die grüne Farbe zeigt an, dass das Bild aus dem Cache-Speicher abgerufen wird .

Die blaue Farbe zeigt an, dass das Bild aus dem Festplattenspeicher abgerufen wird .

Vor dem Loslassen der App löschen oder einstellen false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);falls nicht erforderlich. Danke


1

Ich weiß nicht, wie gut diese Lösung ist, aber es ist definitiv DIE EINFACHE, die ich gerade in meiner App verwendet habe, und sie funktioniert einwandfrei

Sie laden das Bild so

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

Sie können das bekommen bimapwie die

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

Verstecken Sie das nun Bitmapin eine JPGDatei und speichern Sie es im Cache. Unten finden Sie den vollständigen Code zum Abrufen und Zwischenspeichern der Bimap

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

Die get()Methode von musste Piccassoaus irgendeinem Grund in einem separaten Thread aufgerufen werden. Ich speichere das Bild auch in demselben Thread.

Sobald das Bild gespeichert ist, können Sie alle Dateien so erhalten

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

Jetzt finden Sie die gesuchte Datei wie folgt

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

Ich benutze diesen Code und habe gearbeitet, vielleicht nützlich für Sie:

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

                            });
                }
            });

}

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.