Picasso'da disk önbelleğe almayı nasıl kullanırım?


119

Android uygulamamda resim görüntülemek için Picasso kullanıyorum:

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

Hata ayıklamayı etkinleştirdim ve her zaman kırmızı ve yeşil gösteriyor.Ama asla sarı göstermiyor

Şimdi aynı görüntüyü bir dahaki sefere yüklersem ve internet mevcut değilse, görüntü yüklenmez.

Sorular:

  1. Yerel disk önbelleği yok mu?
  2. Aynı görüntüyü birden çok kez kullanacağım için disk önbelleğe almayı nasıl etkinleştiririm.
  3. Android manifest dosyasına biraz disk izni eklemem gerekiyor mu?

Ben de aynı sorunu yaşıyorum. Önbelleğe almayacak!
Jonathan

Beyler, facebook'un Fresco kitaplığına bir göz atmalısınız. Önbellek yönetimi harika.
Michel Fortes

Yanıtlar:


229

Ben öyle yaptım. İyi çalışıyor.

Önce OkHttp'yi uygulama modülünün gradle derleme dosyasına ekleyin:

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'

Sonra genişleyen bir sınıf yapın 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);

    }
}

Manifest dosyasına aşağıdaki gibi ekleyin:

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

</application>

Şimdi Picasso'yu normalde yaptığınız gibi kullanın. Değişiklik yok.

DÜZENLE:

yalnızca önbelleğe alınmış görüntüleri kullanmak istiyorsanız. Kütüphaneyi böyle arayın. NetworkPolicy'yi eklemezsek, resimlerin önbelleğe alınmış olsalar bile tamamen çevrimdışı bir başlangıçta görünmeyeceklerini fark ettim . Aşağıdaki kod sorunu çözmektedir.

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

DÜZENLE # 2

Yukarıdaki kodla ilgili sorun, önbelleği temizlerseniz, Picasso'nun önbellekte çevrimdışı aramaya devam etmesi ve başarısız olmasıdır, aşağıdaki kod örneği yerel önbelleğe bakar, çevrimdışı bulunmazsa çevrimiçi olur ve önbelleği doldurur.

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. , Soruyu cevapladım. Ve çözüm işe yarıyor. Ama kullanabileceğim bu küçük açıklama var. OkHttp belgelerini inceledim ve "önbellek" biriminden bahsetmiyorlar. Öyleyse, bir bilgelik paylaşmak isteyen varsa ... bu iyi bir fırsat.
Sanket Berde

@ArtjomB. evet bu mantıklı. Düzenlendi!
Sanket Berde

5
@SanketBerde: Hızlı not için teşekkürler, ancak görüntünün bellekten yalnızca Uygulama arka planda çalışıyorsa (çevrimdışıyken) geldiğini fark ettim. Uygulamayı kapatırsam, bu çalışan uygulamaları temizler ve ardından uygulamamı tekrar açar, görüntüler Önbellekten yüklenmez. Karşınıza çıkan görüntüyü yüklerken hata varsayılanını ayarladım. Burada ne yanlış olabilir?
TheDevMan

1
Belki Picasso işlerin çalışma şeklini değiştirdi, çünkü benim için okhttp ve ağ politikası olmadan iyi çalışıyor. Yeni bir başlangıçta, görüntüleri anında diskten alır ve ağ çevrimdışı olduğunda, yine de iyi gösterir.
zeeshan

1
okhttp3.OkHttpClientKütüphane ile OkHttp3Downloadersınıf formunu kullanmanız gerekircompile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Chuck

46

1) ilk sorunun cevabı: Picasso Doc for With () yöntemine göre

() İle döndürülen genel varsayılan Picasso örneği, çoğu uygulamaya uygun varsayılanlarla otomatik olarak başlatılır.

  • LRU bellek önbelleği, kullanılabilir uygulama RAM'ının% 15'i
  • 50MB’ye kadar ancak 5MB’den az olmamak üzere% 2 depolama alanı disk önbelleği.

Ancak Disk Cache genel Varsayılan Picasso için işlem yalnızca API 14+ üzerinde kullanılabilir

2) İkinci Soru cevabı: Picassokullanmak HTTPiçin istemci isteğini Disk Cacheoperasyonu Yani sen kendi yapabilir http request headerözelliğine sahip Cache-Contrololan max-age ve kullanmakla varsayılan yerine Picasso Kendi Statik Picasso Örneği oluşturmak

1] HttpResponseCache (Not: Yalnızca API 13+ için çalışır)
2] OkHttpClient (Tüm API'ler için çalışır)

OkHttpClientKendi Statik Picasso sınıfınızı oluşturmak için kullanma örneği :

  • Önce kendi singleton picassonesnenizi almak için yeni bir sınıf oluşturun

    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;
        }
    
    } 
  • picassoyerine kendi singleton nesnenizi kullanınPicasso.With()

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

3) üçüncü sorunun cevabı: Disk Önbelleği işlemleri için herhangi bir disk iznine ihtiyacınız yok

Kaynaklar : Disk önbelleği hakkında Github sorunu , iki Sorular tarafından yanıtlandı Jake-Wharton @ -> Question1 ve Question2


4
Hayır, uygulama kapalıysa bu işe yaramaz. Uygulama zorla durdurulduktan sonra tüm resimler kayboldu.
nbumakov

2
bu bana şu hatayı veriyor:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
CIRCLE

@CIRCLE geç kaldığım için özür dilerim, Örneği kullanmak için önce [okhttp] ( square.github.io/okhttp ) paketini ve [okio] ( github.com/square/okio ) paketini okhttp
indirmeniz gerekiyor

@CIRCLE, [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… ) paketini de
indirmeniz gerekebilir

Bu benim için çalışmıyor. Görüntüdeki konumlarına her kaydırdığımda görüntüler yeniden yükleniyorPager
Charu

21

Önbelleğe alma için, önbelleğe alma ilkesi üzerinde kontrol sahibi olmak için OkHttp engelleyicileri kullanırdım. OkHttp kitaplığında bulunan bu örneğe göz atın.

RewriteResponseCacheControl.java

İşte Picasso ile nasıl kullanacağım -

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
Artık çalışmıyor networkInterceptors () değişmez bir liste döndürür.
noev

1
OkHttp 3.x'te @noev, durdurucu eklemek için oluşturucu desenini (bkz. github.com/square/okhttp/wiki/Interceptors ) kullanabilirsiniz.
Gaurav B

10

En güncel 2.71828 sürümü için Bunlar sizin cevabınız.

S1 : Yerel disk önbelleği yok mu?

A1 : Picasso'da varsayılan önbelleğe alma var ve istek akışı aynen bunun gibi

App -> Memory -> Disk -> Server

Görüntüleriyle ilk karşılaştıkları her yerde, o görüntüyü kullanacaklar ve ardından istek akışını durduracaklar. Yanıt akışı ne olacak? Merak etmeyin, işte burada.

Server -> Disk -> Memory -> App

Varsayılan olarak, genişletilmiş saklama önbelleği için önce bir yerel diskte depolanırlar. Ardından, önbelleğin örnek kullanımı için bellek.

Bunu etkinleştirerek görüntülerin nerede oluştuğunu görmek için Picasso'daki yerleşik göstergeyi kullanabilirsiniz.

Picasso.get().setIndicatorEnabled(true);

Resimlerinizin sol üst köşesinde bir bayrak görünecektir.

  • Kırmızı bayrak, görüntülerin sunucudan geldiği anlamına gelir. (İlk yüklemede önbelleğe alma yok)
  • Mavi bayrak, fotoğrafların yerel diskten geldiği anlamına gelir. (Önbelleğe almak)
  • Yeşil bayrak, görüntülerin bellekten geldiği anlamına gelir. (Örnek Önbelleğe Alma)

S2 : Aynı görüntüyü birden çok kez kullanacağım için disk önbelleğe almayı nasıl etkinleştirebilirim?

A2 : Etkinleştirmenize gerek yok. Bu varsayılandır.

Yapmanız gereken şey, resimlerinizin her zaman taze olmasını istediğinizde bunu DEVRE DIŞI BIRAKMAK . Engelli önbelleğe almanın 2 yolu vardır.

  1. Set .memoryPolicy()için NO_CACHE ve / veya NO_STORE ve akış aşağıdaki gibi görünecektir.

NO_CACHE bellekten resim aramayı atlayacaktır.

App -> Disk -> Server

NO_STORE , görüntüleri ilk yüklerken bellekteki görüntüleri atlayacaktır.

Server -> Disk -> App
  1. Set .networkPolicy()için NO_CACHE ve / veya NO_STORE ve akış aşağıdaki gibi görünecektir.

NO_CACHE diskten resim aramayı atlayacak.

App -> Memory -> Server

NO_STORE , görüntüleri ilk yüklerken diskteki görüntüleri atlayacaktır.

Server -> Memory -> App

Sen edebilirsiniz DEVRE DIŞI tamamen yok görüntüleri önbelleğe alma için de. İşte bir örnek.

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

Tamamen önbelleğe alma ve depolamanın olmadığı akış böyle görünecektir.

App -> Server //Request

Server -> App //Response

Dolayısıyla, uygulama depolama kullanımınızı da küçültmek için buna ihtiyacınız olabilir.

S3 : Android bildirim dosyasına biraz disk izni eklemem gerekir mi?

A3 : Hayır, ancak HTTP isteğiniz için İNTERNET iznini eklemeyi unutmayın.


6

1) Picasso varsayılan olarak önbelleğe sahiptir (bkz. Ahmed hamdy cevabı)

2) Disk önbelleğinden gerçekten görüntü almanız gerekiyorsa ve ardından ağ kendi indiricinizi yazmanızı öneririm:

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

    }
}

Ve OnCreate yöntemindeki tekil uygulamada picasso ile kullanın:

        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) Defalut uygulama önbellek klasörü için izin gerekmez


1

Aşağıdaki kodu ekleyin Application.onCreateve normal olarak kullanın

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

Önce görüntüleri önbelleğe alırsanız, şuraya benzer bir şey yapın: 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);
        }

ve resimlerinizi normal veya disk önbelleğe alma gibi yükleyin

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

Not:

Kırmızı renk, görüntünün ağdan getirildiğini gösterir .

Yeşil renk, görüntünün önbellekten getirildiğini gösterir .

Mavi renk, görüntünün disk belleğinden getirildiğini gösterir .

Uygulamayı yayınlamadan önce false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);gerekli değilse silin veya ayarlayın . thankx


1

Bu çözümün ne kadar iyi olduğunu bilmiyorum ama kesinlikle uygulamamda kullandığım KOLAY BİR çözüm ve iyi çalışıyor

görüntüyü böyle yüklersin

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

Sen alabilirsiniz bimapböyle

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

Şimdi bunu Bitmapbir JPGdosyaya dönüştürün ve önbellekte saklayın, aşağıda bimap'i almak ve önbelleğe almak için eksiksiz kod bulunmaktadır.

 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()yöntem Piccassobazı nedeni gerekli için ayrı iş parçacığı üzerinde çağrılacak, ben aynı iş parçacığı üzerinde de bu görüntüyü kaydederken ediyorum.

Görüntü kaydedildikten sonra tüm dosyaları bunun gibi alabilirsiniz

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

şimdi aradığınız dosyayı aşağıdaki gibi bulabilirsiniz

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

Bu kodu kullandım ve çalıştım, belki sizin için yararlı olabilir:

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

                            });
                }
            });

}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.