Как использовать кеширование диска в Picasso?

Я использую 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);
}

У меня есть возможность отладки и всегда отображается как красный, так и зеленый. Но никогда не отображается желтый цвет

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

Вопросы:

  • Не имеет ли кэш локального диска?
  • Как включить кеширование диска, поскольку я буду использовать одно и то же изображение несколько раз.
  • Нужно ли мне добавить диск в файл манифеста Android?

Ответ 1

Это то, что я сделал. Работает хорошо.

Сначала добавьте 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");
            }
        });
    }
});

Ответ 2

1) ответ на первый вопрос: согласно Метод Picasso для метода With()

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

  • Кэш LRU объемом 15% доступной оперативной памяти
  • Кэш-память емкостью 2% памяти до 50 МБ, но не менее 5 МБ.

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

2) ответ второго вопроса: Picasso используйте клиентский запрос HTTP для Disk Cache операции So, вы можете сделать свой собственный http request header свойство Cache-Control с max-age И создайте свой собственный экземпляр Static Picasso вместо стандартного Picasso. Используя

1] HttpResponseCache (Примечание: работает только для API 13+)
2] OkHttpClient (работает для всех API)

Пример для использования OkHttpClient для создания собственного класса Static Picasso:

  • Сначала создайте новый класс, чтобы получить собственный объект singleton 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;
        }
    
    } 
    
  • используйте собственный объект singleton Picasso Вместо Picasso.With()

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

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

Ссылки: Проблема Github о кэше диска, на два вопроса ответил @jake-whartonQuestion1 и Question2

Ответ 3

Для кэширования я бы использовал перехватчики 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);

Ответ 4

1) Picasso по умолчанию имеет кеш (см. ahmed hamdy answer)

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

    }
}

А в приложении 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

Ответ 5

Я использую этот код и работал, возможно, полезен для вас:

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

                            });
                }
            });

}

Ответ 6

Добавьте код followning в Application.onCreate, затем используйте его normal

    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

Ответ 7

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

Q1: у него нет локального дискового кэша?

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

App -> Memory -> Disk -> Server

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

Server -> Disk -> Memory -> App

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

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

Picasso.get().setIndicatorEnabled(true);

Он покажет флаг в левом верхнем углу ваших фотографий.

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

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

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

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

Q3: мне нужно добавить некоторые права на диск в файл манифеста Android?

A3: Нет, но не забудьте добавить разрешение ИНТЕРНЕТ для вашего HTTP-запроса.

Ответ 8

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