English version - "Universal Image Loader. Part 2 - Configuration"
Предыдущая статья: "Universal Image Loader. Part 1 - Введение"
Все манипуляции проходят посредством класса ImageLoader. Это singletone, поэтому чтобы получить единственный экземпляр класса надо вызвать метод getInstance(). Перед использованием ImageLoader'а по назначению (для отображения картинок), необходимо проинициализировать его конфигурацией - ImageLoaderConfiguration, с помощью метода init(...). Ну а дальше можно со спокойной душой юзать все вариации метода displayImage(...).
В общем, самый простой вариант использования ImageLoader'a (с конфигурацией по умолчанию) представлен ниже:
А теперь рассмотрим полный функционал.
Как вы уже поняли, сначала ImageLoader надо проинициализировать с помощью объекта конфигурации. Т.к. ImageLoader - синглтон, то инициализировать его нужно один раз за запуск приложения. Я бы рекомендовал делать это в перегруженном Application.onCreate(). Повторная инициализация уже проинициализированного ImageLoader'а не будет иметь никакого эффекта.
Значит, создаем конфигурацию, а это объект класса ImageLoaderConfiguration. Создаем с помощью Builder'а:
Итак, конфигурация создана. Теперь ею можно проинициализировать ImageLoader:
Следующая статья: "Universal Image Loader. Part 3 - Usage"
Предыдущая статья: "Universal Image Loader. Part 1 - Введение"
Все манипуляции проходят посредством класса ImageLoader. Это singletone, поэтому чтобы получить единственный экземпляр класса надо вызвать метод getInstance(). Перед использованием ImageLoader'а по назначению (для отображения картинок), необходимо проинициализировать его конфигурацией - ImageLoaderConfiguration, с помощью метода init(...). Ну а дальше можно со спокойной душой юзать все вариации метода displayImage(...).
В общем, самый простой вариант использования ImageLoader'a (с конфигурацией по умолчанию) представлен ниже:
ImageView imageView = ... // вьюха, где будет отображать картинку String imageUrl = ... // URL картинки (н-р: "http://site.com/image.png", "file:///mnt/sdcard/img/image.jpg") ImageLoader imageLoader = ImageLoader.getInstance(); // Получили экземпляр imageLoader.init(ImageLoaderConfiguration.createDefault(context)); // Проинициализировали конфигом по умолчанию imageLoader.displayImage(imageUrl, imageView); // Запустили асинхронный показ картинки
А теперь рассмотрим полный функционал.
Как вы уже поняли, сначала ImageLoader надо проинициализировать с помощью объекта конфигурации. Т.к. ImageLoader - синглтон, то инициализировать его нужно один раз за запуск приложения. Я бы рекомендовал делать это в перегруженном Application.onCreate(). Повторная инициализация уже проинициализированного ImageLoader'а не будет иметь никакого эффекта.
Значит, создаем конфигурацию, а это объект класса ImageLoaderConfiguration. Создаем с помощью Builder'а:
File cacheDir = StorageUtils.getCacheDirectory(context, "UniversalImageLoader/Cache");
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.memoryCacheExtraOptions(480, 800) // width, height
.discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75) // width, height, compress format, quality
.threadPoolSize(5)
.threadPriority(Thread.MIN_PRIORITY + 2)
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024)) // 2 Mb
.discCache(new UnlimitedDiscCache(cacheDir))
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.imageDownloader(new BaseImageDownloader(5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.enableLogging()
.build();
Рассмотрим каждую настройку:- memoryCacheExtraOptions() используется при декодировании изображений в объекты Bitmap. Чтобы не хранить в памяти полноразмерную картинку, она уменьшается до размера, определяемых из значений параметров ImageView, куда грузится картинка: maxWidth и maxHeight (в первую очередь), layout_width и layout_height (во вторую очередь). Если эти параметры не определены (значения fill_parent и wrap_content считаются неопределенными), то тогда берутся размеры, заданные параметрами из данного memoryCacheExtraOptions() ;
- Значения по умолчанию - размеры экрана аппарата
- discCacheExtraOptions() задает параметры для хранения картинок на "дисковом кэше". По умолчанию на дисковом кэше хранятся полноразмерные картинки. С помощью данной настройки можно задать максимальный размер для хранимых картинок, а также формат сжатия и качество (для JPEG).
- threadPoolSize(int) задает размер пула потоков. Каждое задание на загрузку и отображение картинки выполняется в отдельном потоке, а те потоки, в которых происходит загрузка изображений из сети, попадают в пул. Т.о. размер пула определяет количество потоков, работающих одновременно. Задание большого размера пула может существенно снизить скорость работы UI, например в списке может подтормаживать прокрутка.
- Значения по умолчанию - 5
- threadPriority(int) задает приоритет в системе (от 1 до 10) всех потоков, в которых выполняются задачи;
- Значения по умолчанию - 4
- вызов denyCacheImageMultipleSizesInMemory() накладывает запрет на хранение в памяти разных размеров одной и той же картинки. Т.к в дисковом кэше хранятся полноразмерные картинки, а при загрузке в память они уменьшаются до размеров ImageView, в которых они должны отобразиться, то бывают случаи, что одну и ту же картинку необходимо отобразить сначала в маленькой вьюшке, а потом в большой. При этом в памяти будут хранится две Bitmap'ки разных размеров, представляющие одинаковую картинку. Это поведение по умолчанию. Инструкция denyCacheImageMultipleSizesInMemory() обеспечивает удаление из кэша в памяти предыдущего размера загружаемой картинки.
- с помощью memoryCache(...) можно задать реализацию кэша в памяти. Можно воспользоваться готовыми решениями (все они представляют собой реализации кэша ограниченного размера, где при превышении размера кэша из него удаляется объект по определенному алгоритму):
- FIFOLimitedCache (удаляется объект по принципу First-In-First-Out)
- LargestLimitedCache (удаляется самый большой по размеру объект)
- UsingAgeLimitedCache (удаляется объект с самой давней датой доступа)
- UsingFreqLimitedCache (удаляется самый редко используемый объект)
- Значение по умолчанию - UsingFreqLimitedCache с ограничением памяти в 2 Мб
- memoryCacheSize(int) задает максимальный размер кэша в памяти. В этом случае используется кэш по умолчанию - UsingFreqLimitedCache.
- Значение по умолчанию - 2 Мб
- с помощью discCache(...) можно задать реализацию кэша на файловой системе. Можно воспользоваться готовыми решениями (в них файлы, соответствующие определенным URL-ам, именуются как хеш-коды этих URL-ов):
- UnlimitedDiscCache (обычный кэш, без ограничений)
- FileCountLimitedDiscCache (ограниченный по размеру кэш)
- TotalSizeLimitedDiscCache (ограниченный по количеству файлов кэш)
- Значение по умолчанию - UnlimitedDiscCache
- discCacheSize(int) задает максимальный размер кэша на файловой системе. В этом случае используется TotalSizeLimitedDiscCache.
- discCacheFileCount(int) задает максимальное количество файлов в дисковом кэше. В этом случае используется FileCountLimitedDiscCache.
- с помощью discCacheFileNameGenerator(...) можно задать свой генератор имен для файлов закешированных на диске картинок: на входе получается URI картинки, на выходе надо выдать уникальное имя для файла (FileNameGenerator).
Есть готовые варианты:- HashCodeFileNameGenerator - в качестве имени файла берется java хеш-код URI картинки
- Md5FileNameGenerator - в качестве имени файла генерируется хеш-код по алгоритму MD5
- с помощью imageDownloader(...) можно задать свою реализацию загрузчика картинок: нужно представить свою имплементацию интерфейса ImageDownloader.
Можно воспользоваться готовыми реализациями (по надобности их также можно унаследовать):- BaseImageDownloader - используется URLConnection для работы с сетью
- HttpClientImageDownloader - используется HttpClient для работы с сетью
- с помощью defaultDisplayImageOptions(...) можно задать опции отображения картинок, которые будут использоваться для всех вызовов метода displayImage(...), где не были переданы кастомные опции. Подробнее, что это за эти опции, будет рассказано ниже.
- enableLogging() включает подробное логгирование процесса работы ImageLoader'a.
ImageLoaderConfiguration config = ImageLoaderConfiguration.createDefault(context);
Итак, конфигурация создана. Теперь ею можно проинициализировать ImageLoader:
ImageLoader.getInstance().init(config);Вот и все, ImageLoader готов к использованию. Но об этом Я расскажу уже в следующей статье.
Следующая статья: "Universal Image Loader. Part 3 - Usage"
Не могу понять почему происходит именно так. Возможно это известная ситуация. Используестя андроид 2.1. Вот кусок лога
ReplyDelete08-19 20:46:42.249: I/ImageLoader(469): Load image from Internet [http://img...jpg_48x80]
08-19 20:46:42.500: I/global(469): Default buffer size used in BufferedInputStream constructor. It would be better to be explicit if an 8k buffer is required.
На 4.1 картинки показываются. Возможно я что-то пропустил и есть минимальные требования к SDK?
Минимальный API Level - 1.5. Не понял, в чем проблема? Не отображаются картинки на 2.1? В приведенных логах никаких ошибок не вижу. Или это все, что есть в логах?
DeleteДа именно так на android 2.1 картинки не показываются.
DeleteВот лог http://codepaste.ru/11565/
Предпологаю что причина в фабрике SkImageDecoder::Factory returned null
Начиная с android 2.2 картинки отображаются.
А пример проекта с GitHub (который UniversalImageLoaderExample) у вас работает на 2.1? Показывает картинки?
DeleteСкажите а можно добавить в loader свой httpclient? Например, если в приложении уже используется кастомный httpclient как синглтон, чтоб уже можно было его передать в ваш лоадер.
ReplyDeleteДа, можно. Надо в конфигурацию передать свой imageDownloader. Можно воспользоваться HttpClientImageDownloader, который в конструктор принимает HttpClient.
DeleteПодскажите пожалуйста, можно ли указать путь к картинке, которая находится в assets ?
ReplyDeleteт.к. при file:///android_asset/roks.png
выдает ошибку
/android_asset/roks.png (No such file or directory)
хотя файл присутствует. при изменении на file:///mnt/sdcard/ все работает прекрасно
в чем может быть ошибка ?
Путь типа file://... указывает на файловую систему девайса. Assets папка лежит в приложении и не может быть доступна, как обычная папка на файловой системе.
DeleteТут есть два выхода:
- в начале скопировать все файлы из assets на SD карту, а потом доступаться к ним через file://...
- создать свой ImageDownloader (и засетить его в конфигурации), перегрузить InputStream getStreamFromOtherSource(URI imageUri), в нем по URI найти нужный вам файл в assets и перегнать его в InputStream. Для картинок из assets вызывать displayImage(...) c URI типа этого: "assets://roks.png".
Скоро примерчик для второго случая включу в Example проект на GitHub.
Включил.
DeleteПочему может не сохранять картинки в папке для кеша? пермишн стоит. папка существует (перепроверено много раз). Вот мои конфиги
ReplyDeleteImageLoaderConfiguration imageLoaderconfig = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(50, 50) // width, height
.discCacheExtraOptions(50, 50, Bitmap.CompressFormat.PNG, 75)
.threadPoolSize(3)
.threadPriority(Thread.NORM_PRIORITY - 2)
.denyCacheImageMultipleSizesInMemory()
.offOutOfMemoryHandling()
.memoryCache(new UsingFreqLimitedMemoryCache(5 * 1024 * 1024)) // 2 Mb
.discCacheSize(15000)
.discCacheFileCount(50)
.discCache(new UnlimitedDiscCache(cacheDir))
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.enableLogging()
.build();
Readme читали? https://github.com/nostra13/Android-Universal-Image-Loader#useful-info
DeleteКэшировать или не кэшировать картинку выставляется в DisplayImageOptions. В простых опциях - DisplayImageOptions.createSimple() - кэширование ни в памяти, ни на диске не включено.
Также вызовы в вашей конфигурации:
.discCacheSize(15000)
.discCacheFileCount(50)
.discCache(new UnlimitedDiscCache(cacheDir))
перекрывают друг друга. Информация об этом есть в Java доках, и в логах выводятся варнинги. Выбирайте что-то одно.
Ой ёй, извините что так сильно натупил.. Ушел читать доки.
DeleteПривет, спасибо за библиотечку.
ReplyDeleteЕсть один вопрос. У меня почему-то фотки (которые я снимаю и складываю во внутреннее хранилище) отображаются в неправильной ориентации. Они всегда повернуты на 90 градусов.
Вы с этим не сталкивались? Может подскажете как это исправить?
Спасибо.
Привет. Скорее всего это из-за того, что поворот хранится в EXIF метаданных, а UIL никак не учитывает EXIF при отображении (пока). Вы можете создать свой BitmapDisplayer, где будете учитывать поворот, и засетить его в конфигурацию.
DeleteХотя не, тут нужен доступ к файлу картинки. Тогда меняйте исходники или ждите, пока не добавится эта фича. Сколько ждать - не знаю.
DeleteСпасибо Сергей.
ReplyDeleteпри быстрой прокрутки, картинки сбрасываються на дефолтные, как оставить картинки на своих местах ?
ReplyDeleteПодскажите пожалуйста есть ли возможность установить listener на загрузку ряда картинок? Чтоб, к примеру, ProgressDialog скрывался только после загрузки всей очереди , а не после каждой картинки
ReplyDeleteЕсли только вы сами будете отслеживать это в листенере. Счетчик там какой-нибудь заведете или типа того, а листенере проверять, если счетчик равен кол-ву картинок - скрывайте ProgressDialog.
DeleteКэширую изображению вот сюда: cacheDir = StorageUtils.getCacheDirectory(Context)
ReplyDeleteкак из этого каталога определенно получить изображение?
Там они все храняться с непонятными именами..
File image = DiscCacheUtil.findInCache(imageUri, imageLoader.getDiscCache());
DeleteСсылка на изображение содержит кириллицу.
ReplyDeleteВ списке все отображается нормально. В ImageLoaderConfiguration указал memoryCache. Когда пытаюсь найти путь к изображению в кэшэ, использую этот метод
DiscCacheUtil.findInCache(logoUrl.get(position), ImageLoader.getInstance().getDiscCache())
он возвращает null.
В чем может быть проблема?
Читайте Readme - Useful Info, где и как включается кэш.
DeleteВ конфигурации вы лишь настраиваете кэши. А уж кэшировать или нет - это в DisplayImageOptions. И не забывайте разницу между кэшированием в памяти и на диске.
В DisplayImageOptions указал cacheInMemory()
DeleteИ все?
DeleteНет, вот все, что указал:
Deleteoptions = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_image)
.showImageForEmptyUri(R.drawable.image_for_empty_url)
.cacheInMemory()
.cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2).build();
Что-то так и не понял, в чем косяк... Может быть так, что это проблема заключается в том, что путь к картинке содержит кириллицу?
DeleteКонфигурация какая у вас?
DeleteFile cacheDir = StorageUtils.getCacheDirectory(EngineBrowseByOnline.this);
DeleteImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.memoryCacheExtraOptions(100, 100)
.threadPoolSize(5)
.threadPriority(Thread.MIN_PRIORITY + 2)
.offOutOfMemoryHandling()
.memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024))
.discCache(new UnlimitedDiscCache(cacheDir))
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.discCacheFileCount(100)
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()).enableLogging()
.build();
Да, это проблема связана с кириллицей. Пофикшу в следующей версии.
DeleteПока решение для вас такое:
String encodedUrl = Uri.encode(logoUrl.get(position), "@#&=*+-_.,:!?()/~'%");
DiscCacheUtil.findInCache(encodedUrl, ImageLoader.getInstance().getDiscCache());
Спасибо!
DeleteЖду следующей версии..
Сергей, спасибо! Отличная библиотека! Подскажите, а если мне не нужно сохранять картинки в кеше. Как правильно настроить объект?
ReplyDeleteГде не нужно кэшировать? В памяти или на файловой системе?
DeleteВ DisplaytImageOptions есть .cacheInMemory() и .cacheOnDisc(). Если эти опции не включать, то и кэшироваться не будет.
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ImagePagerActivity.this).build();
DeleteimageLoader.init(config);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.resetViewBeforeLoading().cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_INT)
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new FadeInBitmapDisplayer(300))
.build();
Я рисую изображение, показываю его пользователю с помощью DisplayImageOptions. После я рисую уже новое изображение, но сохраняю его с тем же именем и в тоге получаю старую картинку.
Все, понял свою ошибку. Надо было убрать .resetViewBeforeLoading().cacheOnDisc().
DeleteСергей, привет. За библиотеку спасибо! Подскажи, пожалуйста, столкнулся с проблемой. Если изображение уже есть в кэше и по этому же url подсунуть другое изображение, то оно не загрузится, а отобразится старое из кэша.
ReplyDeleteВот, например: Есть файл site.ru/image/sample.jpg на котором изображена буква А, это изображение загрузили и сохранили в кэш. Потом по этому же адресу на веб сервере положили изображение с буквой Б (т.е. содержимое изменилось, а адрес нет). Так вот UIL не загружает новый файл, потому что url не изменился и показывает старое изображение из кэша. Как такое решить?
Надо удалить сперва изображения из кэшей. Посмотри в сторону MemoryCacheUtil и DiscCacheUtil. Есть соответствующие имплементации кэшей (LimitedAge...), которые будут удалять закэшированные картинки через указанное время, тем самым форсируя их повторную загрузку с сервера.
DeleteLimitedAge.. видимо в более поздних версиях библиотеки появилось, у меня версия 1.5.2 :) Буду обновляться, спасибо
Delete