English version - "Universal Image Loader. Part 3 - Usage"
Предыдущие статьи:
В прошлой статье мы проинициализировали ImageLoader конфигурацией и теперь он готов к непосредственному использованию по назначению.
Для этого он имеет 4 перегруженных метода:
Первый вариант - все просто: говорим из какого URL-а загрузить картинку и в какой ImageView отобразить. Опции отображения (DisplayImageOptions) в этом случае будут взяты из конфигурации (defaultDisplayImageOptions(...)).
Второй вариант: вот мы уже можем определить отдельные опции для конкретной задачи. Приведем сначала примерчик создания своих опций:
Третий вариант: в дополнение ко всему вы можете "прослушивать" процесс загрузки и отображения картинки с помощью интерфейса ImageLoadingListener:
Советы и рекомендации
Иходники проекта по прежнему доступны на GitHub.
Предыдущие статьи:
В прошлой статье мы проинициализировали ImageLoader конфигурацией и теперь он готов к непосредственному использованию по назначению.
Для этого он имеет 4 перегруженных метода:
void displayImage(String url, ImageView view) void displayImage(String url, ImageView view, DisplayImageOptions options) void displayImage(String url, ImageView view, ImageLoadingListener listener) void displayImage(String url, ImageView view, DisplayImageOptions options, ImageLoadingListener listener)
Первый вариант - все просто: говорим из какого URL-а загрузить картинку и в какой ImageView отобразить. Опции отображения (DisplayImageOptions) в этом случае будут взяты из конфигурации (defaultDisplayImageOptions(...)).
Второй вариант: вот мы уже можем определить отдельные опции для конкретной задачи. Приведем сначала примерчик создания своих опций:
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_image)
.showImageForEmptyUrl(R.drawable.image_for_empty_url)
.resetViewBeforeLoading()
.cacheInMemory()
.cacheOnDisc()
.decodingType(ImageScaleType.EXACT)
.build();
Да, опять Builder. Как говорилось в первой статье, с помощью DisplayImageOptions мы можем указать:- надо ли отображать картинку-заглушку в ImageView, пока загружается реальная картинка, и какую именно заглушку отображать;
- надо ли отображать какую-либо картинку в ImageView, если URL картинки был передан пустым (null), и какую именно картинку отображать;
- "обнулять" ли ImageView перед началом загрузки;
- надо ли кэшировать загруженную картинку в памяти;
- надо ли кэшировать загруженную картинку на файловой системе.
- декодировать изображение максимально быстро (ImageScaleType.POWER_OF_2) или максимально экономно для оперативной памяти (ImageScaleType.EXACT).
Третий вариант: в дополнение ко всему вы можете "прослушивать" процесс загрузки и отображения картинки с помощью интерфейса ImageLoadingListener:
public interface ImageLoadingListener {
void onLoadingStarted();
void onLoadingFailed();
void onLoadingComplete();
void onLoadingCancelled();
}
Ну а четвертый вариант - самый мощный: можете и опции определить, и "слушать" процесс.Советы и рекомендации
- Для того, чтобы ImageLoader смог выполнить свое предназначение, ему необходимо передать корректные параметры. И речь не столько о URL картинки, сколько об ImageView. Если вы создаете объект ImageView в коде (а не с помощью LayoutInflater), то в передавайте конструктор текущий Activity, а не контекст приложения:
- Параметры maxImageWidthForMemoryCache(...) и maxImageHeightForMemoryCache(...) стоит настраивать в конфигурации только если вы хотите загружать в ImageView картинки размера, превышающего размеры экрана устройства (например для последующего увеличения). Во всех остальных случаях этого делать не нужно: эти параметры по умолчанию учитывают размеры экрана, что позволяет экономить память при работе с Bitmap'ами.
- Устанавливайте размер пула потоков в конфигурации с умом: большой размер пула (> 10) позволит работать множеству потоков одновременно, что может сильно сказаться на отзывчивости UI. Но это можно поправить установкой более низкого приоритета для потоков: чем ниже приоритет, тем отзывчивей UI во время работы ImageLoader'а, но тем долльше грузятся картинки. Отзывчивость UI крайне важна для списков (плавная прокрутка), поэтому стоит поиграться с натройкой параметров threadPoolSize(...) и threadPriority(...) для подбора оптимальной конфигурации для вашего приложения.
- Настройки memoryCacheSize(...) и memoryCache(...) перекрывают друг друга, используйте только одну из них для одного объекта конфигурации.
- Настройки discCacheSize(...), discCacheFileCount(...) и discCache(...) перекрывают друг друга, используйте только одну из них для одного объекта конфигурации.
- Если при использовании ImageLoader'а в приложении вы всегда (или почти всегда) передаете в метод displayImage(...) одни и те же опции загрузки (DisplayImageOptions), то тогда разумным решением будет задать данные опции в конфигурации ImageLoader в качестве опций по умолчанию (метод defaultDisplayImageOptions(...)). Тогда данные опции не обязательно будет каждый раз указывать при вызове displayImage(...), т.к. если в метод явно не переданы опции, то для данной задачи будут использоваться дефолтные опции.
- Большой разницы в скорости между типами декодирования POWER_OF_2 и EXACT нет, но рекомендуется использовать POWER_OF_2 для разного рода списков (где нужно отображать много картинок небольшого размера), а EXACT для галерей (когда нужно отображать картинки в больших размерах).
ImageView imageView = new ImageView(getApplicationContext()); // НЕ ПРАВИЛЬНО! ImageView imageView = new ImageView(MyActivity.this); // правильно ImageView imageView = new ImageView(getActivity()); // правильно (для Fragment'ов)
Иходники проекта по прежнему доступны на GitHub.
Насколько я понял, в новой версии убрали DecodingType.
ReplyDeleteТочнее переименовали в ImageScaleType.
DeleteFAST -> POWER_OF_2
MEMORY_SAVING -> EXACT
Вместо картинки пришёл html (или другой мусор). Это возможно отловить? В логах вижу SkImageDecoder::Factory returned null, но как отловить ошибку декодирования я не вижу.
ReplyDeleteС помощью слушателя:
DeleteimageLoader.displayImage(imageUri, imageView, options, new SimpleImageLoadingListener() {
@Override
public void onLoadingFailed(FailReason failReason) {
switch (failReason) {
case IO_ERROR:
// Вот сюда должен прийти callback в случае ошибки
break;
}
}
});
Не приходит callback. Ошибки при загрузке нету. Простой способ проверить -- указать в качестве адреса картинки http://ya.ru . ImageLoader пришедший html даже кеширует.
DeleteВ новой версии 1.5.2 поправил этот момент. IO_ERROR должен приходить.
DeleteКак можно сделать, чтобы на диск кешировалось постоянно, а не только когда кеш памяти переполнится?
ReplyDeleteС чего вы взяли, что на диск кэшируется только после переполнения памяти?
DeleteПо логам видно, кроме того если после загрузки изображений отключить связь и убить приложение, то не все картинки сохранятся на карточке.
DeleteВсё, я уже разобрался. Надо было .cacheOnDisk указывать перед .cacheInMemory.
Вообще-то кэширование на диске зависит только от вызванной или не вызванной .cacheOnDisk в options. Если не указано, то и кэшировать на диске не будет, даже при "переполнении памяти".
DeleteПорядок вызова опций в options не имеет значения. Как и в конфигурации.
А нормально то, что в коде так получается что строчка : .imageScaleType(ImageScaleType.POWER_OF_2)
ReplyDeleteвыглядит так, что POWER_OF_2 зачеркнутым написанно?
Это значит, что этот параметр устарел и его лучше не использовать (в будущем он будет удален). Всю инфу можно найти в Java доках, если они вам видны. В общем, замените POWER_OF_2 на IN_SAMPLE_POWER_OF_2.
DeleteThis element neither has attached source nor attached Javadoc and hence no Javadoc could be found.
DeleteА как можно увидеть javadoc?
Попробуйте использовать jar-ку, которая в Example проекте. Там в jar-ке сразу исходникик лежат с java-доками.
DeleteНе выходит. Она вообще не присоединяется к проекту - не читается(
DeleteИмя jar-ки?
Deleteuniversal-image-loader-1.7.0-with-src.jar
DeleteА как вы его присоединяете? И какая у вас IDE?
DeleteУ меня Эклипс 4.2.0
DeleteДобавляю через Add JARs... Я что-то не так делаю? =(
Надо jar-ки закидывать в папку libs в корне проекта. Из Build Path поудалять все ссылки на jar-ки, они должны автоматически присоединяться.
DeleteОч. здорово получилось, по-моему!
ReplyDeleteОгромный выбор настроек, меняющих поведение ImageLoader'a + возможность все запустить вообще без настроек.
Прикрутил подгрузку картинок к приложению буквально с первого запуска (а до этого часа полтора читал туториалы и просматривал структуру классов :)).
Все бы так читали туториалы сначала... :) Спасибо.
DeleteЗдравствуйте!
ReplyDeleteУ меня проблема со ссылками вида:
http://tonkosti.ru/images/5/54/Caretta_Bar%26Disco%2C_%D0%BE%D1%82%D0%B5%D0%BB%D1%8C_Adora_Golf_Resort_%D0%91%D0%B5%D0%BB%D0%B5%D0%BA.jpg
Мне выдается ошибка:
libjpeg error 27 from setjmp [0 0]
Могли бы вы подсказать как справиться?
Здравствуйте. Какие это у вас особенные картинки. Дело не в ссылке, а в том, что андроидовский декодер не может задекодить такую картинку. Почему - не знаю.
DeleteМне тут сказали, что CMYK формат не читается ни на каких версиях андроида =(
Deletehttp://stackoverflow.com/questions/7608507/in-android-how-to-decode-a-jpeg-in-cmyk-color-format/7634763#7634763
Hello Sergey, I've a problem with Image Loader (wich is very fine), I just want to display a local drawable from local. So I put this URL into my String array :
ReplyDeletepublic static final String[] IMAGES = new String[]{ "drawable://" + R.drawable.app_icon }
but it doesnt work and I've a null pointer exception..
Can you tell me how display drawable ?
Look here - http://stackoverflow.com/questions/14098993/how-to-use-universal-image-loader-for-loading-resources-locally
Deleteyes but I don't understand why : "drawable://" + R.drawable.app_icon, // Image from drawables doesn't work... do you??
DeleteDid you set ExtendedImageDownloader to configuration?
Deleteyes I'm doing this in UILApplication.java : ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
Delete.threadPriority(Thread.NORM_PRIORITY - 2)
.memoryCacheSize(2 * 1024 * 1024) // 2 Mb
.denyCacheImageMultipleSizesInMemory()
.discCacheFileNameGenerator(new Md5FileNameGenerator())
.imageDownloader(new ExtendedImageDownloader(getApplicationContext()))
.tasksProcessingOrder(QueueProcessingType.LIFO)
.enableLogging() // Not necessary in common
.build();
So..
...Если открыть изображение, пролистать его в любую сторону, то при повороте экрана изображение не сохраняется. А отображается повернутый рисунок, который открывался раньше, словно пролистывания и не было... Где это можно подправить?
ReplyDeleteа... понял.. У Вас в BaseActivity идет переопределение onSaveInstanceState
ReplyDeleteСкажите, а можно как либо сохранить картинку в файловый кэш?
ReplyDeleteДопустим я сделал фотку, хочу её сразу поместить в файловый кэш, можно в принципе сохранить её по пути cacheDir который я задаю при инициализаци ImageLoader-а и прогонять имя теме же NameGenerator-ом, но нет ли более простого решения.
Пока нет.
DeleteКаким образом у Вас в библиотеке обрабатывается ошибка, возникающая в случае, если в дисковом кэше закончилось место или если дисковый кэш располагался на SD-карте и карта была отключена от девайса?
ReplyDeleteЕсли не обрабатывается, тот каким образом есть возможность обработать, а лучше предупредить данные ошибки из вне?
Данные ошибки при использования дискового кэша на SD-карте приводят к тому, что картинки перестают загружаться, а хотелось бы, чтобы библиотека просто продолжала загружать картинки без использования кэша.
Случаи такие не обрабатываются (что не есть хорошо), занес их в планы.
DeleteНа данный момент, чтобы хэндлить случаи отключения SD краты, надо сделать свой кэш (можно унаследоваться от какого-нибудь существующего, и засетить его в конфиг), в котором будет метод типа setCacheDir(File dir). Далее в случае отключения карты - сетить другую папку (StorageUtil.getCacheDirectory() пойдет).
Следить за тем, не закончилось ли место на карте, тоже придется самому, и чистить кэш в этом случае.
Над решением проблемы буду думать, но скоро ли будет - не знаю.
Здравствуйте, я взял Ваш пример, что бы попробовать что и как, и тут нагрянул вопрос, как в него можно прикрутить возможность сохранения текущего изображения, по нажатию кнопки.
ReplyDeleteЕсли вы использовали кэширование на диске, то оно уже сохранено в кэше. Остается только скопировать его куда надо. Посмотрите в сторону DiscCacheUtil.
DeleteЗдраствуйте, подскажите есть ли возможность обновить изображение в кеше. скажем загрузили картинку, она попала в кеш, затем эта картинка поменялась, скажем ориентацию например поменяла. Снова пытаемся загрузить ее, но ImageLoader берет все старую картинку из кеша( как ее обновить в кеше?
ReplyDeleteЧтобы форсировать обновление картинки, надо удалить ее из кэшей сначала. Используйте MemoryCacheUtil и DiscCacheUtil для этого. А также посмотрите в сторону LimitedAgeMemoryCache и LimitedAgeDiscCache, которые через указанное время сами удаляют картинку из кэша.
DeleteУчтите, что UIL сначала смотрит в кэш в памяти, потом в дисковый кэш, и потом загружает картинку из сети, если ее не нашлось в кэшах.
Кэши конфигурятся в конфигурации, а включаются в DisplayImageOptions.
Здаствуйте, подскажите возможно ли получить изображение как BitMap, а не сразу отображать его в ImageView? Дело в том что мне нужно отобразить изображение в ImageView как Background...
ReplyDeleteImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, 0 /* or maxImageWidth */, 0 /* or maxImageHeight */)
DeleteImageLoader.loadImage(..., targetSize, listener)
В листенере в onLoadingComplete(...) получаете готовую битмапку.
Сергей, спасибо за библиотеку. Есть одна проблема. Не могу правильно настроить размер пула потоков. Список подлагивает. по разному пытался настроить.. но как-то без изменений
ReplyDeleteОдна настройка размера пула не спасет, если пролемы в другом. Обязательно для списков используйте переиспользование вьюх. Вопросы вы можете задавать здесь - http://stackoverflow.com/questions/tagged/universal-image-loader
DeleteViewHolder, ViewStub... Всё имеется. Если закоментировать displayImage то список не лагает.
Deleteconfig такой
new ImageLoaderConfiguration.Builder(
getApplicationContext())
.threadPoolSize(20).threadPriority(1)
.defaultDisplayImageOptions(optionsList)
.memoryCache(new LruMemoryCache(150 * 1024 * 1024)).build();
DisplayImageOptions optionsList = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.placeholder)
.showImageOnFail(R.drawable.placeholder)
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.showImageForEmptyUri(R.drawable.placeholder).cacheInMemory()
.cacheOnDisc().build();
.threadPoolSize(20).threadPriority(1) этого нету) уже удалил
DeleteРазмер кэша в памяти - 150 метров? Это вы зря. Максимум 16.
DeleteПовторюсь - всевопросы на StackOverFlow с подробным описанием и примерами кода.
Как можно использовать несколько ImageLoaderConfiguration?
ReplyDeleteМне надо один для списков.. что бы указывать размер высоты и ширины кеша в диск.. а второй для галерей, где надо кешировать полный размер
Если только создать два инстанса ImageLoader'а. Нужно создать свой класс и унаследовать ImageLoader.
DeleteСпасибо за совет.
DeleteПривет. Почему иногда большие фотки обрезаются при загрузки?
ReplyDeleteПривет. Не знаю.
Delete