Как использовать кеширование диска в 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?
user93796
источник
У меня такая же проблема. Он не кешируется!
Джонатан
Ребята, вам стоит взглянуть на Fresco lib в facebook. У него потрясающее управление кешем.
Мишель Фортес

Ответы:

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

    }
}

добавьте его в файл манифеста следующим образом:

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

</application>

Теперь используйте Пикассо, как обычно. Без изменений.

РЕДАКТИРОВАТЬ:

если вы хотите использовать только кешированные изображения. Назовите библиотеку так. Я заметил, что если мы не добавим networkPolicy, изображения не будут отображаться при полностью автономном запуске, даже если они кэшированы . Приведенный ниже код решает проблему.

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

РЕДАКТИРОВАТЬ # 2

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

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, и они не упоминают единицу "кеша". Так что если кто-то захочет поделиться мудростью ... это хорошая возможность.
Sanket Berde
@ArtjomB. да, в этом есть смысл. Отредактировано!
Sanket Berde
5
@SanketBerde: Спасибо за небольшую заметку, но я понял, что изображение появляется из памяти только в том случае, если приложение работает в фоновом режиме (в автономном режиме). если я закрою приложение, это очистит запущенные приложения, затем снова открою мое приложение, изображения не загружаются из кеша. Я установил ошибку при загрузке изображения по умолчанию, которое появляется. Что здесь могло быть не так?
TheDevMan
1
Возможно, Пикассо изменил принцип работы, потому что для меня он отлично работает без okhttp и сетевой политики. При новом запуске он мгновенно получает изображения с диска, а когда сеть отключена, он по-прежнему показывает их нормально.
zeeshan
1
С okhttp3.OkHttpClientбиблиотекой вы должны использовать OkHttp3Downloaderформу класса compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Чак
46

1) ответ на первый вопрос: по методу Picasso Doc for With ()

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

  • Кэш памяти LRU составляет 15% доступной ОЗУ приложения
  • Дисковый кеш-память размером 2% до 50 МБ, но не менее 5 МБ.

Но Disk Cache операция для глобального Пикассо по умолчанию доступна только в API 14+.

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

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
@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

Вот как я бы использовал это с Пикассо -

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
Больше не работает networkInterceptors () возвращает неизменяемый список.
noev
1
@noev в OkHttp 3.x вы можете использовать шаблон построителя (см. github.com/square/okhttp/wiki/Interceptors ) для добавления перехватчиков.
Gaurav B
10

Для самой последней версии 2.71828 Это ваш ответ.

Q1 : У него нет кеша на локальном диске?

A1 : в Picasso есть кеширование по умолчанию, и поток запросов такой же

App -> Memory -> Disk -> Server

Везде, где они впервые встретили свое изображение, они будут использовать это изображение, а затем остановят поток запросов. А как насчет потока ответов? Не волнуйтесь, вот оно.

Server -> Disk -> Memory -> App

По умолчанию они сначала сохраняются на локальном диске для расширенного кэша хранения. Затем память для экземпляра использования кеша.

Вы можете использовать встроенный индикатор в Picasso, чтобы увидеть, где формируются изображения, включив его.

Picasso.get().setIndicatorEnabled(true);

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

  • Красный флаг означает, что изображения поступают с сервера. (Без кеширования при первой загрузке)
  • Синий флаг означает, что фотографии взяты с локального диска. (Кэширование)
  • Зеленый флаг означает, что изображения взяты из памяти. (Кэширование экземпляра)

Q2 : Как включить кэширование диска, если я буду использовать один и тот же образ несколько раз?

A2 : Вам не нужно его включать. Это по умолчанию.

Что вам нужно сделать, так это ОТКЛЮЧИТЬ, если вы хотите, чтобы ваши изображения всегда были свежими. Есть 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

Таким образом, вам может потребоваться это также, чтобы минимизировать использование хранилища вашего приложения.

В3 : нужно ли мне добавить разрешение на диск в файл манифеста Android?

A3 : Нет, но не забудьте добавить разрешение INTERNET для вашего 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) Не требуются разрешения для папки кеша приложений defalut

TSM
источник
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);если не требуется. Thankx

Камар
источник
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файл и сохраните в кеше, ниже приведен полный код для получения 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();
                    })

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

qbait
источник
Glide в 5 раз больше кода, чем Picasso. Если вы уже используете okhttp, лучше использовать Пикассо
Александр Фарбер
2
Обе библиотеки используют кеширование, но кешируют по-разному: medium.com/@multidots/glide-vs-picasso-930eed42b81d
Orri