Как использовать кеширование диска в 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?
7 ответов
Это то, что я сделал. Работает хорошо.
Сначала добавьте 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");
}
});
}
});
1) ответ на первый вопрос: по методике Пикассо Док для С ()
Глобальный экземпляр Picasso по умолчанию, возвращаемый функцией with(), автоматически инициализируется значениями по умолчанию, которые подходят для большинства реализаций.
- Кэш-память LRU 15% доступной оперативной памяти приложения
- Дисковый кэш объемом 2%, до 50 МБ, но не менее 5 МБ.
Но Disk Cache
Операция для глобального Пикассо по умолчанию доступна только в 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
Для самой последней версии 2.71828 Это ваш ответ.
Q1: у него нет локального дискового кэша?
A1: в Picasso есть кеширование по умолчанию и поток запросов, подобный этому
App -> Memory -> Disk -> Server
Где бы они ни встретили свое изображение в первую очередь, они будут использовать это изображение, а затем останавливают поток запросов. Как насчет потока ответов? Не волнуйся, вот оно.
Server -> Disk -> Memory -> App
По умолчанию они сначала сохраняются на локальном диске для расширенного хранения кеша. Затем память, для использования экземпляра кеша.
Вы можете использовать встроенный индикатор в Picasso, чтобы увидеть, где образуются изображения, включив это.
Picasso.get().setIndicatorEnabled(true);
Он покажет флаг в левом верхнем углу ваших фотографий.
- Красный флаг означает, что изображения поступают с сервера. (Нет кэширования при первой загрузке)
- Синий флаг означает, что фотографии пришли с локального диска. (Кэширование)
- Зеленый флаг означает, что изображения приходят из памяти. (Кэширование экземпляра)
В2: Как включить кэширование на диске, так как я буду использовать одно и то же изображение несколько раз?
A2: Вам не нужно включать его. Это по умолчанию.
Что вам нужно сделать, это отключить его, когда вы хотите, чтобы ваши изображения всегда были свежими. Существует 2 способа отключения кэширования.
- Установлен
.memoryPolicy()
NO_CACHE и / или NO_STORE, и поток будет выглядеть следующим образом.
NO_CACHE пропустит поиск изображений из памяти.
App -> Disk -> Server
NO_STORE пропустит сохранение изображений в памяти при первой загрузке изображений.
Server -> Disk -> App
- Установлен
.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-запроса.
Для кэширования я бы использовал перехватчики OkHttp, чтобы получить контроль над политикой кэширования. Проверьте этот пример, который включен в библиотеку OkHttp.
Вот как бы я использовал это с Пикассо -
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) Пикассо по умолчанию имеет кеш (см. Ответ Ахмеда Хэмди)
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 используйте его с Пикассо:
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) Нет необходимости в разрешениях для дефолта кеша папки приложения
Я не знаю, насколько хорошо это решение, но это определенно ЛЕГКОЕ, которое я только что использовал в своем приложении, и оно работает нормально
вы загружаете изображение вот так
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
}
Добавить следующий код в 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
Я использую этот код и работал, может быть, полезно для вас:
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...");
}
});
}
});
}
У меня была та же проблема, и я использовал библиотеку Glide. Кеш там не из коробки. https://github.com/bumptech/glide