Як використовувати кешування диска в Picasso?


119

Я використовую Picasso для відображення зображення в моєму додатку для 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);
}

У мене включена налагодження, і вона завжди відображається або червоною, і зеленою. Але ніколи не показується жовтим

Тепер, якщо я завантажую те саме зображення наступного разу, і Інтернет недоступний, зображення не завантажується.

Запитання:

  1. Немає локального кеш-диска?
  2. Як увімкнути кешування диска, оскільки я буду використовувати одне і те ж зображення кілька разів.
  3. Чи потрібно мені додати якийсь дозвіл диска на файл маніфесту Android?

У мене таке ж питання. Це не буде кешувати!
Джонатан

Хлопці, погляньте на фреску у фейсбуці. Управління кешем - дивовижне.
Мішель Фортес

Відповіді:


229

Це я і зробив. Добре працює.

Спочатку додайте OkHttp до файлу збірки gradle модуля програми:

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'

Потім зробіть клас розширення 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 наступним чином:

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

</application>

Тепер використовуйте Picasso як зазвичай. Ніяких змін.

Редагувати:

якщо ви хочете використовувати лише кешовані зображення Називайте бібліотеку так. Я помітив, що якщо ми не додамо networkPolicy, зображення не відображатимуться під час автономного запуску, навіть якщо вони кешовані . Код нижче вирішує проблему.

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

ЗРІД №2

Проблема з вищевказаним кодом полягає в тому, що якщо ви очистите кеш, Пікассо буде продовжувати шукати його в режимі офлайн у кеші та виходити з ладу, наступний приклад коду розглядає локальний кеш, якщо його не знайти в режимі офлайн, він виходить в Інтернет і поповнює кеш.

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. , Я відповів на питання. І рішення дійсно працює. Але є це невелике уточнення, яке я можу використати. Я пройшов документацію OkHttp, і вони не згадують одиницю "кешу". Тож якщо хтось хотів би поділитися якоюсь мудрістю ... це хороша можливість.
Санкет Берде

@ArtjomB. так, це має сенс. Відредаговано!
Санкет Берде

5
@SanketBerde: Дякую за швидку примітку, але я зрозумів, що зображення виходить із пам'яті лише у тому випадку, якщо додаток працює у фоновому режимі (у режимі офлайн). якщо я закриваю програму, це зрозуміло, що запущені програми, потім знову відкрити додаток, зображення не завантажуються з кешу. Я встановив зображення завантаження за замовчуванням, яке з’являється. Що тут може бути не так?
TheDevMan

1
Можливо, Пікассо змінив, як все працює, бо для мене це прекрасно працює без okhttp та мережевої політики. По-новому, він моментально отримує зображення з диска, а коли мережа перебуває в офлайн-режимі, вона все одно показує їх добре.
Зеешан

1
З okhttp3.OkHttpClientбібліотекою ви повинні використовувати OkHttp3Downloaderформу класу compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Чак

46

1) відповідь на перше запитання: відповідно до методу Picasso Doc for With ()

Глобальний екземпляр Picasso, повернений з (), автоматично ініціалізується за замовчуванням, що підходить для більшості реалізацій.

  • Кеш пам'яті LRU становить 15% доступної оперативної пам’яті
  • Дисковий кеш-пам'ять на 2% місця зберігання до 50 МБ, але не менше 5 МБ.

Але Disk Cache операція для глобального Picasso за замовчуванням доступна лише в API 14+

2) відповідь на друге питання: Picassoвикористовувати HTTPзапит клієнта до Disk Cacheоперації Таким чином , ви можете зробити свій власний http request headerмає властивість Cache-Controlзmax-age І створити свій власний статичний екземпляр Picasso замість Picasso за замовчуванням, використовуючи

1] HttpResponseCache (Примітка: працює лише для API 13+)
2] OkHttpClient (працює для всіх API)

Приклад використання OkHttpClientдля створення власного статичного класу Пікассо:

  • Спочатку створіть новий клас, щоб отримати власний синглтон picasso об’єкт

    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;
        }
    
    } 
  • використовуйте власний синглтон picasso об'єкт ЗамістьPicasso.With()

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

3) відповідь на третє запитання: вам не потрібно жодних дозволів на диск для операцій кешування диска

Посилання : Github питання про кеші диска , два питання була дана відповідь @ Jake-Wharton -> Question1 і question2


4
Ні, це не працює, якщо додаток було закрито. Після того, як додаток було застосовано, всі зображення зникли.
nbumakov

2
це дає мені цю помилку:FATAL EXCEPTION: main java.lang.NoClassDefFoundError: com.squareup.okhttp.OkHttpClient
ЦИРК

@CIRCLE вибачте за запізнення. Щоб використовувати приклад, потрібно завантажити перший пакет [okhttp] ( square.github.io/okhttp ) та пакет [okio] ( github.com/square/okio ), який використовуєokhttp
ahmed hamdy

@CIRCLE, можливо, вам доведеться завантажити пакет [okhttp-urlconnection] ( mvnrepository.com/artifact/com.squareup.okhttp/… ) також
ахмед Хамді

Це не працює для мене. Зображення перезавантажуються щоразу, коли я прокручую їх місце в viewPager
Чару

21

Для кешування я б використовував перехоплювачі OkHttp, щоб отримати контроль над кешичною політикою. Ознайомтеся з цим зразком, що входить до бібліотеки OkHttp.

RewriteResponseCacheControl.java

Ось як я використовував би це з 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
Більше не працює мережаInterceptors () повертає незмінний список.
noev

1
@noev в OkHttp 3.x ви можете використати модель конструктора (див. github.com/square/okhttp/wiki/Interceptors ), щоб додати перехоплювачі.
Гаурав Б

10

Для найновішої версії 2.71828 Це ваша відповідь.

Q1 : Немає локального кеш-диска?

A1 : У Picasso є кешування за замовчуванням, і запит подається саме так

App -> Memory -> Disk -> Server

Де б вони не зустріли своє зображення спочатку, вони використовуватимуть це зображення, а потім зупинятимуть потік запиту. А як щодо потоку відповідей? Не хвилюйтесь, ось це.

Server -> Disk -> Memory -> App

За замовчуванням вони спочатку зберігатимуться на локальному диску для розширеного кешу зберігання. Потім пам'ять, наприклад, використання кешу.

Ви можете використовувати вбудований індикатор у Picasso, щоб побачити, де утворюються зображення, включивши це.

Picasso.get().setIndicatorEnabled(true);

Він з’явиться прапор у верхньому лівому куті ваших фотографій.

  • Червоний прапор означає, що зображення надходять із сервера.(Немає кешування при першому завантаженні)
  • Синій прапор означає, що фотографії приходять з локального диска.(Кешування)
  • Зелений прапор означає, що зображення надходять із пам'яті. (Керування екземплярами)

Q2 : Як увімкнути кешування диска, оскільки я буду використовувати одне і те ж зображення кілька разів?

А2 : Вам не потрібно це вмикати. Це за замовчуванням.

Що вам потрібно зробити, це БЕЗКОШТОВНО коли ви хочете, щоб ваші зображення завжди були свіжими. Існує двосторонній відключений кешування.

  1. Набір .memoryPolicy()для no_cache і / або NO_STORE і потік буде виглядати наступним чином .

NO_CACHE не буде шукати зображення з пам'яті.

App -> Disk -> Server

NO_STORE пропустить збереження зображень у пам'яті при першому завантаженні зображень.

Server -> Disk -> App
  1. Набір .networkPolicy()для no_cache і / або NO_STORE і потік буде виглядати наступним чином .

NO_CACHE не буде шукати зображення з диска.

App -> Memory -> Server

NO_STORE буде пропускати збереження зображень на диску при першому завантаженні зображень.

Server -> Memory -> App

Ви не можете відключити жодне, щоб повністю не кешувати зображення. Ось приклад.

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

Потік без кешування та зберігання не виглядатиме так.

App -> Server //Request

Server -> App //Response

Отже, вам може знадобитися це і для мінімізації використання вашого додатка для зберігання.

Q3 : Чи потрібно додати деякий дозвіл диска до файлу маніфесту Android?

A3 : Ні, але не забудьте додати ІНТЕРНЕТ дозвіл на ваш HTTP-запит.


6

1) Пікассо за замовчуванням має кеш-пам'ять (див. Відповідь Ахмеда Хамді)

2) Якщо ви дійсно повинні сфотографуватися з кеш-диска, а потім із мережі, рекомендую написати власний завантажувач:

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

    }
}

А в Application singleton в методі OnCreate використовуйте його з 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) Немає дозволів, необхідних для папки кешу програми, що дефактує


1

Додайте наступний код, Application.onCreateа потім використовуйте його як звичайне

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

Якщо ви кешуєте зображення спочатку, зробіть щось подібне в 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);
        }

і завантажуйте свої зображення як звичайне або кешування диска

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

Примітка:

Червоний колір означає, що зображення витягується з мережі .

Зелений колір вказує на те, що зображення виймається з кеш-пам'яті .

Синій колір вказує на те, що зображення витягується з пам'яті диска .

Перед випуском програми видаліть або встановіть її false picasso.setLoggingEnabled(true);, picasso.setIndicatorsEnabled(true);якщо цього не потрібно. Дякую


1

Я не знаю, наскільки це гарне рішення, але це, безумовно, ЛЕГИЙ Я щойно використаний у своєму додатку, і він працює чудово

ви завантажуєте зображення таким чином

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

Ви можете отримати bimapподібне

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

Тепер перекрийте це Bitmapу JPGфайл та збережіть у кеші, нижче - повний код для отримання бімапи та кешування

 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()метод Piccassoдля якої - то причини потрібно назвати на окремому потоці, я рятую цей образ також на тому ж потоці.

Після збереження зображення ви можете отримати всі подібні файли

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

тепер ви можете знайти потрібний файл, як нижче

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

Я використовую цей код і працював, можливо, корисний для вас:

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

                            });
                }
            });

}

-3

У мене була така ж проблема, і я використав бібліотеку Glide. Кеш там поза коробкою. https://github.com/bumptech/glide


Glide на 5 разів більше коду, ніж у Пікассо. Коли вже користуєтесь okhttp, краще скористатися Пікассо
Олександр Фарбер

2
Обидві бібліотеки використовують кешування , але кеш по- різному: medium.com/@multidots/glide-vs-picasso-930eed42b81d
Оррі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.