Android - сохранение изображений, загруженных из Интернета
У меня возник вопрос, связанный с тем, следует ли мне (и как) хранить изображения, загруженные из Интернета. Скажем, я звоню в веб-службу из своего приложения для Android. В этой веб-службе я получаю URL-адрес для изображения в Интернете. Я загружаю и показываю это изображение в левой части элемента списка в ListView. Мой вопрос: какой метод я должен использовать для возможного хранения изображения? Должен ли я:
- Сохраните его на SDCard, проверяя, существует ли он, когда создается ListView (при последующих запросах) и повторно загружается (при одновременном обновлении изображения, если он изменяется).
- Храните его в кеше с помощью Context.getCacheDir(), но, возможно, придется повторно загружать его чаще, так как я не могу полагаться на изображение, находящееся в кеше.
- Всегда загружайте его и никогда не храните изображение.
Сами файлы изображений довольно малы, но я ожидаю, что некоторые пользователи могут иметь десятки этих маленьких изображений, загруженных/сохраненных. Что будет лучше всего работать и/или какой предпочтительный метод?
В качестве побочного вопроса, должен ли я сначала загрузить все изображения в моем ListView (и, возможно, заблокировать пользовательский интерфейс в течение некоторого времени) или загрузить их асинхронно, но пока показывать графику заметок (что может быть немного более "уродливым" ")? Какой стандарт здесь?
Ответы
Ответ 1
О том, где хранить:
Ответ зависит от того, что скачано и сколько. Тогда вы делаете выбор.
Например: если вы загружаете что-то временное, меньшее количество (меньше удаленных наборов) и размер (меньше памяти) и является локальным для Activity, вам следует рассмотреть возможность хранения изображений в памяти с помощью SoftReferences. SoftReferences может привести к повторным просмотрам, но поскольку количество элементов невелико, оно должно быть доступно.
Однако, если количество загружаемых элементов превышает определенный порог (что означает больше выборок и памяти), вы должны рассмотреть возможность сокращения выборки, а также потребления памяти Runtime путем их кеширования. Здесь вы можете либо сохранить их на Sdcard, либо во временное хранилище (каталоги кешей, локальные для приложения). Для элементов, которые являются маленькими и имеют смысл только в контексте Приложения (например, миниатюр), пользователь в основном не будет использовать его вне вашего приложения. Таким образом, вы можете хранить такие вещи в каталоге кеша. Лучшая часть их использования заключается в том, что вам не нужно очищать беспорядок. Он обрабатывается автоматически. Это может привести к повторным выборам.
Однако, если загруженные элементы имеют большой размер и могут стоять отдельно от контекста приложения, такого как изображения, видео, аудиоклипы, то SDcard должен быть вашим вариантом.
Вы также должны прочитать: Обработка больших растровых изображений во избежание ошибки OOM во время BitmapFactory.decodeStream(..)
Обратите внимание, что вы также можете проверить, может ли здесь помочь база данных. Смотрите это
Некоторые соображения при ленивой загрузке элементов в ListView:
Вы должны выполнять загрузку в фоновом режиме, а не блокировать поток пользовательского интерфейса. Вы должны рассмотреть возможность отображения временного изображения во время загрузки элемента. Это очевидно во многих приложениях. Например, пример реализации ленивой загрузки см. В этом.
Кроме того, для больших списков вы можете реализовать шаблон SlowAdapter (проверить демонстрации API).
Он в основном загружает файлы, пока список прокручивается.
Примеры проектов, которые могут вам помочь:
В проекте Romain Guy Shelves используется два уровня кэширования, в которых он использует кеш в памяти (HashMap, содержащий SoftReferences) и хранилище на Sdcard. Просмотр исходного кода здесь
Есть также некоторые библиотеки с открытым исходным кодом, которые Марк Мерфи написал (CWAC) и DroidFu, которые могут помочь здесь.
Удачи!
Ответ 2
Что касается вашего "бокового вопроса" - я думаю, что загрузка их асинхронно была бы предпочтительным поведением, тем более, что вам нужно учитывать, что с сетевыми транзакциями это может быть не просто "блокировка пользовательского интерфейса в течение некоторого времени", но "заблокировать пользовательский интерфейс навсегда" в случае, если он никогда не загружается.
Если вы считаете, что это уродливое поведение, вы можете установить таймер (1 или 2 секунды), который дает несколько изображений, которые можно загрузить, и если они все загружены или таймер истек, идите вперед и покажите пользовательский интерфейс в любом случае с изображениями-заполнителями и оставите нагрузку асинхронно. В любом случае это будет способ предотвратить постоянные блокировки.
Что касается первой части вашего вопроса, я думаю, что она несколько зависит от контекста и типа отображаемых изображений. С большинством веб-ресурсов, я думаю, что 2-й вариант был бы предпочтительным, поскольку вы, разумеется, не хотите скачивать несколько раз в тот же сеанс, но вы, вероятно, тоже не хотите есть постоянное хранилище.
Ответ 3
Что касается вашего "бокового вопроса" - я думаю, что загрузка их асинхронно была бы предпочтительным поведением, тем более, что вам нужно учитывать, что с сетевыми транзакциями это может быть не просто "блокировка пользовательского интерфейса в течение некоторого времени", но "заблокировать пользовательский интерфейс навсегда" в случае, если он никогда не загружается.
Чтобы избежать этого, если мы говорим о WebImageView с помощью droid-fu, вы можете изменить функцию ImageLoader.java initialize() на этот
static int alive=-1;
public static synchronized void initialize(Context context) {
if (executor == null) {
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
}
else if(alive==executor.getActiveCount()){
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
}
alive=executor.getActiveCount();
if (imageCache == null) {
imageCache = new ImageCacheHope(context, 25, 5);
}
}
Ответ 4
Я написал Android Image Manager, который прозрачно выполняет кеширование (память и диск). Код находится на Github https://github.com/felipecsl/Android-ImageManager