Как я могу кэшировать изображения после их загрузки из Интернета?
Ответы:
А теперь изюминка: используйте системный кеш.
URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
Bitmap bitmap = (Bitmap)response;
}
Предоставляет как память, так и кеш флэш-памяти, совместно используемый браузером.
гр. Я бы хотел, чтобы кто-нибудь сказал мне это до того, как я написал свой собственный кеш-менеджер.
connection.getContent()
всегда возвращает для меня InputStream, что я делаю не так?
Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
Что касается элегантного connection.setUseCaches
решения, приведенного выше: к сожалению, без дополнительных усилий оно не сработает. Вам нужно будет установить ResponseCache
using ResponseCache.setDefault
. В противном случае бит HttpURLConnection
молча игнорируется setUseCaches(true)
.
Подробнее см. Комментарии вверху FileResponseCache.java
:
(Я бы разместил это в комментарии, но мне явно не хватает ТАКОЙ кармы.)
HttpResponseCache
, вы можете обнаружить, что HttpResponseCache.getHitCount()
возвращается 0. Я не уверен, но я думаю, что это потому, что запрашиваемый вами веб-сервер в этом случае не использует кэширующие заголовки. Чтобы кеширование все равно работало, используйте connection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
.
.getContent()
метода, потому что ответы 304 не имеют связанного тела ответа по стандарту RFC.
Преобразуйте их в растровые изображения, а затем либо сохраните их в коллекции (HashMap, List и т. Д.), Либо вы можете записать их на SD-карту.
При сохранении их в пространстве приложения с использованием первого подхода вы можете захотеть обернуть их вокруг java.lang.ref.SoftReference, особенно если их количество велико (чтобы они были собраны мусором во время кризиса). Однако это может привести к перезагрузке.
HashMap<String,SoftReference<Bitmap>> imageCache =
new HashMap<String,SoftReference<Bitmap>>();
запись их на SD-карту не требует перезагрузки; просто разрешение пользователя.
Uri
ссылку на путь, которую вы можете передать, ImageView
и другие пользовательские представления. Потому что каждый раз вы compress
теряете качество. Конечно, это верно только для алгоритмов с потерями. Этот метод также позволит вам даже хранить хэш файла и использовать его в следующий раз , когда вы запрашиваете для файла с сервера через If-None-Match
и ETag
заголовков.
Используйте LruCache
для эффективного кэширования изображений. Вы можете прочитать LruCache
с сайта разработчика Android
Я использовал нижеприведенное решение для загрузки и кеширования изображений в android. Вы можете выполнить следующие шаги:
ШАГ 1:
сделайте класс именованным ImagesCache
. Я использовалSingleton object for this class
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
public class ImagesCache
{
private LruCache<String, Bitmap> imagesWarehouse;
private static ImagesCache cache;
public static ImagesCache getInstance()
{
if(cache == null)
{
cache = new ImagesCache();
}
return cache;
}
public void initializeCache()
{
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024);
final int cacheSize = maxMemory / 8;
System.out.println("cache size = "+cacheSize);
imagesWarehouse = new LruCache<String, Bitmap>(cacheSize)
{
protected int sizeOf(String key, Bitmap value)
{
// The cache size will be measured in kilobytes rather than number of items.
int bitmapByteCount = value.getRowBytes() * value.getHeight();
return bitmapByteCount / 1024;
}
};
}
public void addImageToWarehouse(String key, Bitmap value)
{
if(imagesWarehouse != null && imagesWarehouse.get(key) == null)
{
imagesWarehouse.put(key, value);
}
}
public Bitmap getImageFromWarehouse(String key)
{
if(key != null)
{
return imagesWarehouse.get(key);
}
else
{
return null;
}
}
public void removeImageFromWarehouse(String key)
{
imagesWarehouse.remove(key);
}
public void clearCache()
{
if(imagesWarehouse != null)
{
imagesWarehouse.evictAll();
}
}
}
ШАГ 2:
создайте другой класс с именем DownloadImageTask, который используется, если растровое изображение недоступно в кеше, он загрузит его отсюда:
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{
private int inSampleSize = 0;
private String imageUrl;
private BaseAdapter adapter;
private ImagesCache cache;
private int desiredWidth, desiredHeight;
private Bitmap image = null;
private ImageView ivImageView;
public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight)
{
this.adapter = adapter;
this.cache = ImagesCache.getInstance();
this.desiredWidth = desiredWidth;
this.desiredHeight = desiredHeight;
}
public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight)
{
this.cache = cache;
this.ivImageView = ivImageView;
this.desiredHeight = desireHeight;
this.desiredWidth = desireWidth;
}
@Override
protected Bitmap doInBackground(String... params)
{
imageUrl = params[0];
return getImage(imageUrl);
}
@Override
protected void onPostExecute(Bitmap result)
{
super.onPostExecute(result);
if(result != null)
{
cache.addImageToWarehouse(imageUrl, result);
if(ivImageView != null)
{
ivImageView.setImageBitmap(result);
}
else if(adapter != null)
{
adapter.notifyDataSetChanged();
}
}
}
private Bitmap getImage(String imageUrl)
{
if(cache.getImageFromWarehouse(imageUrl) == null)
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = inSampleSize;
try
{
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
InputStream stream = connection.getInputStream();
image = BitmapFactory.decodeStream(stream, null, options);
int imageWidth = options.outWidth;
int imageHeight = options.outHeight;
if(imageWidth > desiredWidth || imageHeight > desiredHeight)
{
System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight);
inSampleSize = inSampleSize + 2;
getImage(imageUrl);
}
else
{
options.inJustDecodeBounds = false;
connection = (HttpURLConnection)url.openConnection();
stream = connection.getInputStream();
image = BitmapFactory.decodeStream(stream, null, options);
return image;
}
}
catch(Exception e)
{
Log.e("getImage", e.toString());
}
}
return image;
}
ШАГ 3. Использование вашего Activity
илиAdapter
Примечание: если вы хотите загрузить изображение с URL-адреса из Activity
Class. Используйте второй конструктор DownloadImageTask
, но если вы хотите отображать изображение из Adapter
первого конструктора DownloadImageTask
(например, у вас есть изображение, ListView
и вы устанавливаете изображение из адаптера)
ИСПОЛЬЗОВАНИЕ ОТ ДЕЯТЕЛЬНОСТИ:
ImageView imv = (ImageView) findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class.
cache.initializeCache();
String img = "your_image_url_here";
Bitmap bm = cache.getImageFromWarehouse(img);
if(bm != null)
{
imv.setImageBitmap(bm);
}
else
{
imv.setImageBitmap(null);
DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor.
imgTask.execute(img);
}
ИСПОЛЬЗОВАНИЕ ОТ АДАПТЕРА:
ImageView imv = (ImageView) rowView.findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();
cache.initializeCache();
String img = "your_image_url_here";
Bitmap bm = cache.getImageFromWarehouse(img);
if(bm != null)
{
imv.setImageBitmap(bm);
}
else
{
imv.setImageBitmap(null);
DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor.
imgTask.execute(img);
}
Заметка:
cache.initializeCache()
вы можете использовать этот оператор в самом первом Activity вашего приложения. После того, как вы инициализировали кеш, вам никогда не нужно будет инициализировать его каждый раз, если вы используете ImagesCache
instance.
Я никогда не умею объяснять, но надеюсь, что это поможет новичкам понять, как использовать кеш LruCache
и его использование :)
РЕДАКТИРОВАТЬ:
Теперь есть очень известные библиотеки, известные как Picasso
и, Glide
которые можно использовать для очень эффективной загрузки изображений в приложении для Android. Попробуйте эту очень простую и полезную библиотеку Picasso для Android и Glide для Android . Вам не нужно беспокоиться об изображениях в кеше.
Picasso позволяет без проблем загружать изображения в ваше приложение - часто в одной строке кода!
Glide, как и Пикассо, может загружать и отображать изображения из многих источников, а также заботится о кэшировании и сохранении низкого уровня памяти при выполнении манипуляций с изображениями. Он использовался официальными приложениями Google (например, приложением для Google I / O 2015) и так же популярен, как и Picasso. В этой серии мы собираемся изучить различия и преимущества Glide над Picasso.
Вы также можете посетить блог, чтобы узнать о различиях между Glide и Picasso.
if(cache == null)
что решило мою проблему! :)
Скачать изображение и сохранить на карту памяти можно так.
//First create a new URL object
URL url = new URL("http://www.google.co.uk/logos/holiday09_2.gif")
//Next create a file, the example below will save to the SDCARD using JPEG format
File file = new File("/sdcard/example.jpg");
//Next create a Bitmap object and download the image to bitmap
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
//Finally compress the bitmap, saving to the file previously created
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));
Не забудьте добавить разрешение на Интернет в свой манифест:
<uses-permission android:name="android.permission.INTERNET" />
Я бы подумал об использовании кеша изображений droidfu. Он реализует как кэш изображений в памяти, так и на диске. Вы также получаете WebImageView, который использует преимущества библиотеки ImageCache.
Вот полное описание droidfu и WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/
Я пробовал SoftReferences, они слишком агрессивно используются в Android, и я чувствовал, что их нет смысла использовать
SoftReference
s. LruCache
Вместо этого они рекомендуют использовать их .
Как предположил Thunder Rabbit, ImageDownloader лучше всего подходит для этой работы. Я также нашел небольшую вариацию класса по адресу:
http://theandroidcoder.com/utilities/android-image-download-and-caching/
Основное различие между ними заключается в том, что ImageDownloader использует систему кэширования Android, а модифицированный использует внутреннее и внешнее хранилище в качестве кэширования, сохраняя кэшированные изображения на неопределенный срок или до тех пор, пока пользователь не удалит их вручную. Также автор упоминает совместимость с Android 2.1.
Это хороший улов Джо. В приведенном выше примере кода есть две проблемы: одна - объект ответа не является экземпляром Bitmap (когда мой URL-адрес ссылается на jpg, например http: \ website.com \ image.jpg, его
org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream).
Во-вторых, как указывает Джо, кеширование не происходит без настройки кэша ответов. Разработчикам Android остается откатывать собственный кеш. Вот пример этого, но он только кэширует в памяти, что на самом деле не является полным решением.
http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/
API кэширования URLConnection описан здесь:
http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html
Я все еще думаю, что это нормальное решение, чтобы пойти по этому пути, но вам все равно нужно написать кеш. Звучит весело, но я лучше напишу особенности.
Об этом есть специальная запись в официальном разделе обучения Android: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Раздел довольно новый, его не было, когда задавали вопрос.
Предлагаемое решение - использовать LruCache. Этот класс был представлен в Honeycomb, но он также включен в библиотеку совместимости.
Вы можете инициализировать LruCache, установив максимальное количество записей, и он автоматически отсортирует их для вас и очистит менее используемые, когда вы превысите лимит. В остальном он используется как обычная карта.
Пример кода с официальной страницы:
private LruCache mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
Раньше SoftReferences были хорошей альтернативой, но уже не, цитируя официальную страницу:
Примечание. В прошлом популярной реализацией кэша памяти был кеш растровых изображений SoftReference или WeakReference, однако это не рекомендуется. Начиная с Android 2.3 (уровень API 9) сборщик мусора более агрессивно собирает мягкие / слабые ссылки, что делает их довольно неэффективными. Кроме того, до Android 3.0 (уровень API 11) резервные данные растрового изображения хранились в собственной памяти, которая не высвобождалась предсказуемым образом, что могло привести к кратковременному превышению приложением пределов памяти и сбою.
Рассмотрите возможность использования универсальной библиотеки Image Loader от Сергея Тарасевича . В комплекте:
Универсальный загрузчик изображений позволяет подробно управлять кешем для загруженных изображений со следующими конфигурациями кеша:
UsingFreqLimitedMemoryCache
: Наименее часто используемое растровое изображение удаляется при превышении предельного размера кэша.LRULimitedMemoryCache
: Последнее использованное растровое изображение удаляется при превышении предельного размера кэша.FIFOLimitedMemoryCache
: Правило FIFO используется для удаления при превышении предельного размера кэша.LargestLimitedMemoryCache
: Самый большой растровый рисунок удаляется при превышении предельного размера кэша.LimitedAgeMemoryCache
: Кэшированный объект удаляется, когда его возраст превышает определенное значение .WeakMemoryCache
: Кэш памяти со слабыми ссылками на растровые изображения.Простой пример использования:
ImageView imageView = groupView.findViewById(R.id.imageView);
String imageUrl = "http://site.com/image.png";
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
imageLoader.displayImage(imageUrl, imageView);
В этом примере используется значение по умолчанию UsingFreqLimitedMemoryCache
.
Что на самом деле сработало для меня, так это установка ResponseCache в моем основном классе:
try {
File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { }
а также
connection.setUseCaches(true);
при загрузке растрового изображения.
http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html
Google libs-for-android имеет хорошие библиотеки для управления кешем изображений и файлов.
Некоторое время я боролся с этим; ответы, использующие SoftReferences, будут терять свои данные слишком быстро. Ответы, предлагающие создать экземпляр RequestCache, были слишком беспорядочными, к тому же я так и не смог найти полный пример.
Но ImageDownloader.java у меня прекрасно работает. Он использует HashMap до тех пор, пока не будет достигнута емкость или пока не истечет время ожидания очистки, затем все будет перемещено в SoftReference, тем самым используя лучшее из обоих миров.
Предлагаю IGNITION это даже лучше, чем Droid fu
https://github.com/kaeppler/ignition
https://github.com/kaeppler/ignition/wiki/Sample-applications
Еще более поздний ответ, но я написал Android Image Manager, который прозрачно обрабатывает кеширование (память и диск). Код находится на Github https://github.com/felipecsl/Android-ImageManager
Поздний ответ, но я полагал , что я должен добавить ссылку на мой сайт , потому что я написал учебник , как сделать тайник изображения для андроид: http://squarewolf.nl/2010/11/android-image-cache/ обновления: страница была переведена в офлайн, так как источник был устаревшим. Я присоединяюсь к @elenasys в ее совете по использованию Ignition .
Итак, всем, кто сталкивается с этим вопросом и не нашел решения: надеюсь, вам понравится! = D
Поздний ответ, но я думаю, что эта библиотека очень поможет с кешированием изображений: https://github.com/crypticminds/ColdStorage .
Просто аннотируйте ImageView с помощью @LoadCache (R.id.id_of_my_image_view, "URL_to_downlaod_image_from), и он позаботится о загрузке изображения и загрузке его в представление изображения. Вы также можете указать изображение-заполнитель и загрузку анимации.
Подробная документация аннотации представлена здесь: - https://github.com/crypticminds/ColdStorage/wiki/@LoadImage-annotation