Jak korzystać z buforowania dysku w programie Picasso?


119

Używam Picassa do wyświetlania obrazu w mojej aplikacji na Androida:

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

Mam włączone debugowanie i zawsze pokazuje albo czerwony i zielony, ale nigdy nie pokazuje żółtego

Jeśli następnym razem załaduję ten sam obraz, a internet nie jest dostępny, obraz nie zostanie załadowany.

Pytania:

  1. Czy nie ma lokalnej pamięci podręcznej na dysku?
  2. Jak włączyć buforowanie dysku, ponieważ będę wielokrotnie używać tego samego obrazu.
  3. Czy muszę dodać uprawnienia do dysku do pliku manifestu systemu Android?

Mam ten sam problem. Nie będzie buforować!
Jonathan

Chłopaki, powinniście spojrzeć na Fresco lib na Facebooku. Zarządzanie pamięcią podręczną jest niesamowite.
Michel Fortes

Odpowiedzi:


229

To właśnie zrobiłem. Działa dobrze.

Najpierw dodaj OkHttp do pliku kompilacji Gradle modułu aplikacji:

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'

Następnie dodaj rozszerzenie klasy 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);

    }
}

dodaj go do pliku manifestu w następujący sposób:

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

</application>

Teraz użyj Picassa tak, jak zwykle. Bez zmian.

EDYTOWAĆ:

jeśli chcesz używać tylko obrazów z pamięci podręcznej. Zadzwoń do biblioteki w ten sposób. Zauważyłem, że jeśli nie dodamy NetworkPolicy, obrazy nie będą wyświetlane w trybie całkowicie offline, nawet jeśli są buforowane . Poniższy kod rozwiązuje problem.

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

EDYCJA # 2

Problem z powyższym kodem polega na tym, że jeśli wyczyścisz pamięć podręczną, Picasso będzie nadal szukał go w trybie offline w pamięci podręcznej i zawiedzie, poniższy przykład kodu patrzy na lokalną pamięć podręczną, jeśli nie zostanie znaleziony w trybie offline, przechodzi w tryb online i uzupełnia pamięć podręczną.

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. , Odpowiedziałem na pytanie. Rozwiązanie działa. Ale jest takie małe wyjaśnienie, którego mogę użyć. Przejrzałem dokumentację OkHttp i nie wspominają o jednostce „cache”. Więc jeśli ktoś chciałby podzielić się mądrością… to dobra okazja.
Sanket Berde

@ArtjomB. tak, to ma sens. Edytowano!
Sanket Berde

5
@SanketBerde: Dzięki za krótką notatkę, ale zorientowałem się, że obraz wychodzi z pamięci tylko wtedy, gdy aplikacja działa w tle (w trybie offline). jeśli zamknę aplikację, wyczyść uruchomione aplikacje, a następnie ponownie otwórz moją aplikację, obrazy nie ładują się z pamięci podręcznej. Ustawiłem błąd domyślnego ładowania obrazu, który się zbliża. Co tu może być nie tak?
TheDevMan

1
Może Picasso zmienił sposób, w jaki to działa, ponieważ dla mnie działa dobrze bez okhttp i zasad sieciowych. Po nowym uruchomieniu natychmiast pobiera obrazy z dysku, a gdy sieć jest offline, nadal pokazuje je dobrze.
zeeshan

1
Z okhttp3.OkHttpClientbiblioteką musisz skorzystać z OkHttp3Downloaderformularza zajęć compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck

46

1) odpowiedź na pierwsze pytanie: zgodnie z metodą Picassa Doc for With ()

Globalna domyślna instancja Picassa zwrócona przez metodę () jest automatycznie inicjowana z ustawieniami domyślnymi, które są odpowiednie dla większości implementacji.

  • Pamięć podręczna LRU stanowiąca 15% dostępnej pamięci RAM aplikacji
  • Pamięć podręczna na dysku 2% do 50 MB, ale nie mniej niż 5 MB.

Ale Disk Cache operacja dla globalnego Default Picasso jest dostępna tylko w API 14+

2) odpowiedź na drugie pytanie: Picassoużyj HTTPżądania klienta do Disk Cachedziałania Więc możesz stworzyć własną http request headerwłaściwość has za Cache-Controlpomocą max-age I utworzyć własną statyczną instancję Picassa zamiast domyślnego Picassa.

1] HttpResponseCache (uwaga: działa tylko dla API 13+)
2] OkHttpClient (działa dla wszystkich API)

Przykład użycia OkHttpClientdo stworzenia własnej statycznej klasy Picassa:

  • Najpierw utwórz nową klasę, aby uzyskać własny picassoobiekt 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;
        }
    
    } 
  • użyj własnego picassoobiektu singleton zamiastPicasso.With()

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

3) odpowiedź na trzecie pytanie: nie potrzebujesz żadnych uprawnień dyskowych do operacji pamięci podręcznej dysku

Referencje : Github Emisja o cache dysku , dwa pytania zostało odebrane przez @ jake-Wharton -> question1 i question2


4
Nie, to nie działa, jeśli aplikacja została zamknięta. Po wymuszonym zatrzymaniu aplikacji wszystkie obrazy zniknęły.
nbumakov

2
to daje mi ten błąd:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCLE

@CIRCLE przepraszam za spóźnienie, aby skorzystać z przykładu, musisz najpierw pobrać pakiet [okhttp] ( square.github.io/okhttp ) i pakiet [okio] ( github.com/square/okio ), który był używany przezokhttp
ahmed hamdy

@CIRCLE może być konieczne pobranie pakietu [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… )
ahmed hamdy

To nie działa dla mnie. Obrazy są ładowane za każdym razem, gdy przewijam do ich pozycji w widoku .Pager
Charu

21

Do buforowania użyłbym przechwytywaczy OkHttp, aby uzyskać kontrolę nad polityką buforowania. Zapoznaj się z tym przykładem zawartym w bibliotece OkHttp.

RewriteResponseCacheControl.java

Oto, jak bym go użył z Picassem -

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
Już nie działa networkInterceptors () zwraca niezmienną listę.
noev

1
@noev w OkHttp 3.x możesz użyć wzorca budującego (patrz github.com/square/okhttp/wiki/Interceptors ), aby dodać przechwytywacze.
Gaurav B

10

W przypadku najnowszej wersji 2.71828 Oto Twoja odpowiedź.

P1 : Czy nie ma lokalnej pamięci podręcznej na dysku?

A1 : W Picassie istnieje domyślne buforowanie, a przepływ żądań jest taki sam

App -> Memory -> Disk -> Server

Gdziekolwiek napotkają pierwszy obraz, użyją go, a następnie zatrzymają przepływ żądania. A co z przepływem odpowiedzi? Nie martw się, oto jest.

Server -> Disk -> Memory -> App

Domyślnie będą najpierw zapisywać na dysku lokalnym dla rozszerzonej pamięci podręcznej. Następnie pamięć, na przykład użycie pamięci podręcznej.

Możesz użyć wbudowanego wskaźnika w Picasso, aby zobaczyć, gdzie powstają obrazy, włączając tę ​​opcję.

Picasso.get().setIndicatorEnabled(true);

Wyświetli flagę w lewym górnym rogu Twoich zdjęć.

  • Czerwony flaga oznacza, że ​​obrazy pochodzą z serwera. (Brak buforowania przy pierwszym załadowaniu)
  • niebieski flaga oznacza, że ​​zdjęcia pochodzą z dysku lokalnego. (Buforowanie)
  • Zielona flaga oznacza, że ​​obrazy pochodzą z pamięci.(Buforowanie instancji)

P2 : Jak włączyć buforowanie dysku, ponieważ będę wielokrotnie używać tego samego obrazu?

A2 : Nie musisz go włączać. To jest domyślne.

Musisz wyłączyć to, jeśli chcesz, aby obrazy były zawsze świeże. Istnieje 2 sposoby wyłączenia buforowania.

  1. Ustaw .memoryPolicy()na NO_CACHE i / lub NO_STORE a przepływ będzie wyglądał następująco.

NO_CACHE pominie wyszukiwanie obrazów z pamięci.

App -> Disk -> Server

NO_STORE pominie zapisywanie obrazów w pamięci przy pierwszym załadowaniu obrazów.

Server -> Disk -> App
  1. Ustaw .networkPolicy()na NO_CACHE i / lub NO_STORE, a przepływ będzie wyglądał następująco.

NO_CACHE pominie wyszukiwanie obrazów z dysku.

App -> Memory -> Server

NO_STORE pominie zapisywanie obrazów na dysku przy pierwszym załadowaniu obrazów.

Server -> Memory -> App

Nie możesz WYŁĄCZYĆ ani dla całkowitego braku buforowania obrazów. Oto przykład.

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

Przepływ bez buforowania i przechowywania będzie wyglądał następująco.

App -> Server //Request

Server -> App //Response

Może więc być to konieczne do zminimalizowania użycia pamięci aplikacji.

P3 : Czy muszę dodać uprawnienia do dysku do pliku manifestu systemu Android?

A3 : Nie, ale nie zapomnij dodać pozwolenia INTERNETOWEGO dla twojego żądania HTTP.


6

1) Picasso ma domyślnie pamięć podręczną (patrz odpowiedź Ahmed Hamdy)

2) Jeśli naprawdę musisz pobrać obraz z pamięci podręcznej dysku, a następnie z sieci, polecam napisanie własnego downloadera:

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

    }
}

A w aplikacji singleton w metodzie OnCreate użyj go z 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) Brak uprawnień wymaganych do domyślnego folderu pamięci podręcznej aplikacji


1

Dodaj następujący kod, Application.onCreatea następnie używaj go normalnie

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

Jeśli najpierw buforujesz obrazy, zrób coś takiego w 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);
        }

i załaduj swoje obrazy jak normalnie lub z pamięcią podręczną dysku

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

Uwaga:

Kolor czerwony oznacza, że ​​obraz jest pobierany z sieci .

Kolor zielony oznacza, że ​​obraz jest pobierany z pamięci podręcznej .

Kolor niebieski oznacza, że ​​obraz jest pobierany z pamięci dyskowej .

Przed wydaniem aplikacji usuń ją lub ustaw false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);jeśli nie jest to wymagane. Dziękuję


1

Nie wiem, jak dobre jest to rozwiązanie, ale zdecydowanie jest to ŁATWE, którego właśnie użyłem w mojej aplikacji i działa dobrze

ładujesz obraz w ten sposób

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

Możesz dostać bimapcoś takiego

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

Teraz ukryj to Bitmapw JPGpliku i zapisz w pamięci podręcznej, poniżej znajduje się pełny kod do pobrania bimapy i buforowania jej

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

get()metoda Piccassoz jakiegoś powodu trzeba było nazywane na osobnym wątku, ja ratuję ten obraz również w tym samym wątku.

Po zapisaniu obrazu możesz pobrać wszystkie takie pliki

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

teraz możesz znaleźć plik, którego szukasz, jak poniżej

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

Używam tego kodu i zadziałało, być może przydatne dla Ciebie:

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

                            });
                }
            });

}

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.