English version - "Universal Image Loader. Part 1 - Introduction"
При разработке приложений под Android довольно часто можно столкнуться с задачей отображения некоего графического контента из интернета. Поэтому снова и снова приходится обеспечивать загрузку изображений из сети, их обработку и отображение в условиях ограниченности оперативной памяти. И несмотря на однородность задачи каждый новый проект накладывает на задачу свои специфические требования: возможно нужно организовать кэширование загруженных картинок; если картинки довольно большие, то необходимо обеспечить эффективную работу с памятью для предотвращения злополучной ошибки OutOfMemoryError; возможно во время загрузки изображения нужно показывать картинку-заглушку; а может одну и ту же картинку надо будет отображать в разных размерных вариациях; и т.д. В результате тратятся время и ресурсы на адаптацию программного кода под специфические нужды. Именно данная проблематика и подтолкнула меня к созданию библиотеки с открытым исходным кодом - Universal Image Loader, целью которой является универсализация решения вышеописанной задачи в виде гибкого и конфигурируемого инструмента.
На данный момент библиотеку можно использовать повсеместно, где надо загрузить и отобразить (и возможно ещё закэшировать) картинку из интернета или из файловой системы смартфона. Классические примеры возможности применения ImageLoader'а - это списки, таблицы, галереи, где необходимо отображать изображения из сети.
Главные фишки ImageLoader'а:
Глобальные настройки:
Возможностей конфигурирования достаточно, но это не тот случай, наподобие "главного принципа UNIX": "Вы можете сконфигурировать ВСЁ. И вы БУДЕТЕ конфигурировать все." :) В случае ImageLoader'а, вы можете настраивать все, но это отнюдь не обязательно: конфигурация по умолчанию всегда доступна и подходит для большинства случаев.
Особенности реализации
Немного слов о структуре проекта. Каждая задача на загрузку и отображение картинки (а это, забегая вперед, вызов ImageLoader.displayImage(imageView, imageUrl)) выполняется в отдельном потоке, кроме случаев, если картинка находится в кэше в памяти - тогда она просто сразу отображается. Существует отдельная очередь потоков, куда попадают задачи, если нужная картинка закэширована на файловой системе. Если же нужной картинки нет в кэше, то задача-поток попадает в пул потоков. Т.о. быстрому отображению закэшированных картинок ничего не препятствует.
Алгоритм обработки задачи упрощенно представлен на схеме:
Можно условно выделить основные действующие лица в проекте:
В следующей части Я расскажу непосредственно о Universal Image Loader API, а также дам некоторые советы и рекомендации по использованию библиотеки.
Следующие статьи:
Исходники проекта доступны здесь.
При разработке приложений под Android довольно часто можно столкнуться с задачей отображения некоего графического контента из интернета. Поэтому снова и снова приходится обеспечивать загрузку изображений из сети, их обработку и отображение в условиях ограниченности оперативной памяти. И несмотря на однородность задачи каждый новый проект накладывает на задачу свои специфические требования: возможно нужно организовать кэширование загруженных картинок; если картинки довольно большие, то необходимо обеспечить эффективную работу с памятью для предотвращения злополучной ошибки OutOfMemoryError; возможно во время загрузки изображения нужно показывать картинку-заглушку; а может одну и ту же картинку надо будет отображать в разных размерных вариациях; и т.д. В результате тратятся время и ресурсы на адаптацию программного кода под специфические нужды. Именно данная проблематика и подтолкнула меня к созданию библиотеки с открытым исходным кодом - Universal Image Loader, целью которой является универсализация решения вышеописанной задачи в виде гибкого и конфигурируемого инструмента.
На данный момент библиотеку можно использовать повсеместно, где надо загрузить и отобразить (и возможно ещё закэшировать) картинку из интернета или из файловой системы смартфона. Классические примеры возможности применения ImageLoader'а - это списки, таблицы, галереи, где необходимо отображать изображения из сети.
Главные фишки ImageLoader'а:
- асинхронная загрузка и отображение изображений из интернета или с SD-карты;
- возможность кэширования загруженных картинок в памяти и/или на файловой системе устройства;
- возможность отслеживания процесса загрузки посредством "слушателей"
- эффективная работа с памятью при кэшировании картинок в памяти;
- широкие возможности настройки инструмента под свои нужды.
Глобальные настройки:
- максимальный размер кэшируемых в памяти картинок;
- тайм-аут для установки соединения и загрузки картинки;
- максимальное количество потоков для загрузки изображений, работающих одновременно;
- приоритет потоков по загрузке и отображению картинок;
- программная реализация дискового кэша (можно выбрать одну из готовых реализаций или создать свою собственную);
- программная реализация кэша в памяти (можно выбрать одну из готовых реализаций или создать свою собственную);
- опции загрузки изображения по умолчанию
- отображать ли картинку-"заглушку" в ImageView, пока реальная картинка грузится? (если да, то нужно указать эту "заглушку");
- отображать ли какую-либо картинку в ImageView, если URL картинки был передан пустым? (если да, то нужно указать эту картинку);
- кэшировать ли загруженную картинку в памяти?
- кэшировать ли загруженную картинку на файловой системе?
- тип декодирования изображения (максимально быстрый или максимально экономный для памяти).
Возможностей конфигурирования достаточно, но это не тот случай, наподобие "главного принципа UNIX": "Вы можете сконфигурировать ВСЁ. И вы БУДЕТЕ конфигурировать все." :) В случае ImageLoader'а, вы можете настраивать все, но это отнюдь не обязательно: конфигурация по умолчанию всегда доступна и подходит для большинства случаев.
Особенности реализации
Немного слов о структуре проекта. Каждая задача на загрузку и отображение картинки (а это, забегая вперед, вызов ImageLoader.displayImage(imageView, imageUrl)) выполняется в отдельном потоке, кроме случаев, если картинка находится в кэше в памяти - тогда она просто сразу отображается. Существует отдельная очередь потоков, куда попадают задачи, если нужная картинка закэширована на файловой системе. Если же нужной картинки нет в кэше, то задача-поток попадает в пул потоков. Т.о. быстрому отображению закэшированных картинок ничего не препятствует.
Алгоритм обработки задачи упрощенно представлен на схеме:
Можно условно выделить основные действующие лица в проекте:
- вышеупомянутые очередь и пул потоков;
- кэш в памяти;
- дисковый кэш;
- декодер изображений, который перегоняет файлы картинок в объекты Bitmap.
Следующие статьи:
Исходники проекта доступны здесь.
Здравствуйте Сергей! Как с Вами можно связаться? есть вопросы. Спасибо
ReplyDeleteЗдравствуйте. На GitHub есть мой email - вот здесь
DeleteМолодьцы! Еще не юзал, но по статьям это то, что мне нужно.
ReplyDeleteСпасибо. Хорошая библиотека. Не подскажите каким образом можно сделать автоматическое слайдшоу? В каком направлении "копать"?
ReplyDeleteViewPager и Timer вам в руки.
DeleteСпасибо. Я к этому же и пришел))
ReplyDeleteПытаюсь использовать вместе с facebook api и выдает гору ошибок, может подскажете в чем моя ошибка?
ReplyDeleteИзвините, но что-то меня подводят мои экстрасенсорные способности в последнее время. Так что не смогу помочь.
DeleteПодскажите, а как можно масштабировать изображение? Дело в том, что при загрузки, оно становится меньше размера экрана и находится по середине. Изображение находится во внутренней памяти телефона, когда просматриваю стандартными средствами android - изображение на весь экран.
ReplyDeleteUIL не предоставляет функциональность масштабирования. Для этого вам нужно дополнительно использовать другие библиотеки (PhotoView, ImageViewTouch, gesture-image-view, ...). Также надо будет указать UIL, чтобы он не уменьшал картинки при декодировании: в опциях .imageScaleType(ImageScaleType.NONE).
DeleteЗдравствуйте, отличная библиотека, спасибо! А не подскажите как можно добавить функциональность double-click zoom?
ReplyDeleteДля этого надо использовать сторонние библиотеки: PhotoView, ImageViewTouch, gesture-image-view,...
Deletehttp://stackoverflow.com/questions/13398288/image-zoom-issue-with-universal-image-loader-and-view-pager
Отличная библиотека, спасибо! а можно URI с Drawable использовать?
ReplyDeleteМожно, но не нужно. https://github.com/nostra13/Android-Universal-Image-Loader#acceptable-uris-examples
DeleteДобрый день, огромное спасибо за библиотеку!
ReplyDeleteВопрос: предпочтительнее изображения использовать из assets нежели из drawable я так понимаю ?
Добрый. Да, лучше. А почему не использовать ImageView.setImageResource(...)?
DeleteИ как можно сделать донат, не юзая пэйпал ? )
ReplyDeleteWebMoney.
DeleteГде не искал, не нашел самого просто примера : как отобразить картинку из урла в ImageView.. помогите пожалуйста !
ReplyDeleteВсе круто.. я был пьян и было поздно.. Спасибо Вам за прекрасный труд !
DeleteПожалуйста :)
DeleteThis comment has been removed by the author.
ReplyDeleteСергей, а есть возможность сделать resize на bitmap, после того как картинка скачалась с интернета, или хотя бы размер битмапа узнать что бы сделать изменить layout params.
ReplyDeleteнапример:
if (response.getBitmap() != null) {
int bmWidth = response.getBitmap().getWidth(), bmHeight = response.getBitmap().getHeight();
float ratio = (float) bmWidth / (float) bmHeight;
holder.eventImage.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (displayWidth / ratio)));
holder.eventImage.setImageBitmap(response.getBitmap());
Посмотрите в сторону DisplayImageOptions.imageScaleType(...). Узнать размеры битмапы можно в коллбэке onLoadingComplete(...).
DeleteThis comment has been removed by the author.
ReplyDeleteВ вашем примере происходит утечка памяти, как вы это объясните?
ReplyDeleteOne instance of "org.apache.harmony.xnet.provider.jsse.TrustManagerImpl" loaded by "" occupies 242 576 (10,75%) bytes. The memory is accumulated in one instance of "java.util.HashMap$HashMapEntry[]" loaded by "".
Keywords
org.apache.harmony.xnet.provider.jsse.TrustManagerImpl
java.util.HashMap$HashMapEntry[]
http://bit.ly/1bVVtRT
DeleteЯ полностью поддерживаю, то, что вы делаете! Но анализатор памяти указал на возможные утечки выдал:
DeleteOne instance of "org.apache.harmony.xnet.provider.jsse.TrustManagerImpl" loaded by "" occupies 242 576 (10,75%) bytes. The memory is accumulated in one instance of "java.util.HashMap$HashMapEntry[]" loaded by "".
Keywords
org.apache.harmony.xnet.provider.jsse.TrustManagerImpl
java.util.HashMap$HashMapEntry[]
Как бы не понятно, откуда ноги растут у этой java.util.HashMap$HashMapEntry[]
DeleteСкорее всего это memoryCache, и один элемент занял 242Kb. Почему там утечка - не совсем понятно.
Привет , хорошая библиотека спасибо , у меня ошибка
ReplyDelete02-24 01:30:46.290: E/dalvikvm-heap(2044): Out of memory on a 2048016-byte allocation.
02-24 01:30:46.298: I/dalvikvm(2044): "uil-pool-1-thread-2" prio=3 tid=19 RUNNABLE
02-24 01:30:46.306: I/dalvikvm(2044): | group="main" sCount=0 dsCount=0 obj=0x41f18d20 self=0x5791a918
02-24 01:30:46.306: I/dalvikvm(2044): | sysTid=2271 nice=13 sched=0/0 cgrp=apps/bg_non_interactive handle=1090281488
02-24 01:30:46.306: I/dalvikvm(2044): | state=R schedstat=( 895751973 1427551255 5208 ) utm=65 stm=23 core=0
02-24 01:30:46.313: I/dalvikvm(2044): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
02-24 01:30:46.313: I/dalvikvm(2044): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:623)
02-24 01:30:46.313: I/dalvikvm(2044): at com.nostra13.universalimageloader.core.decode.BaseImageDecoder.decode(BaseImageDecoder.java:78)
02-24 01:30:46.313: I/dalvikvm(2044): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.decodeImage(LoadAndDisplayImageTask.java:285)
02-24 01:30:46.313: I/dalvikvm(2044): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.tryLoadBitmap(LoadAndDisplayImageTask.java:241)
02-24 01:30:46.313: I/dalvikvm(2044): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.run(LoadAndDisplayImageTask.java:141)
02-24 01:30:46.313: I/dalvikvm(2044): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
02-24 01:30:46.313: I/dalvikvm(2044): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
02-24 01:30:46.313: I/dalvikvm(2044): at java.lang.Thread.run(Thread.java:856)
Тестировал на Nexus 5 , Nexus 4 , Tab 2 , Library version 1.9.2
lib 1.9.1
DeleteДобрый вечер.
ReplyDeleteА возможно ли добавить метод в imageLoader который будет возвращать drawable или Bitmap по url.
В зависимости от того как вы их храните в памяти.
Т.к. есть возможность вызывать load, а забрать drawable - нет
Аля
ImageLoader.getInstance().getDrawable(url);
or
ImageLoader.getInstance().getBitmap(url);
loadImageSync(...) возвращает Bitmap по URL. Bitmap легко можно обернуть в BitmapDrawable, чтобы получить drawable.
DeleteЗдраствуйте! Нужна помощь...никак не могу встроить ProgressBar в ListView. Задумка такая, пока идет загрузка картинок включается ProgressBar, после того как они загрузились включается ListView, а ProgressBar выключается! По принципу GridView....
ReplyDeleteСергей, спасибо за отличную библиотеку! Все прекрасно работает.
ReplyDeleteНо есть одна проблемка, больше касается медленных устройств. При прокрутке списка происходит подтормаживание, рывки из-за загрузки изображений, хотя они и производятся в фоновых потоках, но по всей видимости отнимают процессорное время, это несколько раздражает пользователей. Сейчас идет тенденция на увеличение плавности интерфейса Андроид-приложений и это правильно.
Напрашивается решение, что нужно как-то блокировать, приостанавливать загрузку на время прокрутки и возобновлять её только, когда прокрутка заканчивается.
Есть какие мысли по этому поводу?
Если есть какие готовые решения, буду рад за подсказку.
Посмотрите в сторону PauseOnScrollListener. Пример использовани есть в example проекте.
Deleteу меня такой вопрос при парсинге xml sax parser из rss все выводится коректно а вот картинке в постах нет можно ли с помощью Uneversal image loader релизовать это
ReplyDeleteНе понимаю вопроса.
DeleteСпасибо, отличная библиотека!
ReplyDeleteПожалуйста)
DeleteМожно как то совместить вашу библиотеку и PhotoView(https://github.com/chrisbanes/PhotoView)? Что то не придумывается как это сделать. Хотелось во ViewPager грузитить картинки UIL и что бы PhotoView их ресайзила. PhotoView берет картинки из drawable, uri, resource или bitmap
ReplyDeleteМожно, просто передайвайте в ImageLoader.displayImage(...) ваш PhotoView.
DeleteСпасибо за ответ, попробую)
DeleteДа, работает, спасибо)
DeleteПри пролистывании ListView, когда картинка грузится в первый раз в кэш, заметны фризы.
ReplyDeleteКакой параметр отвечает за "плавность" подгрузки картинок в фоне?
Каким образом можно узнать имя фотографии, подгруженную в текущий момент времени?
ReplyDeleteХочу сделать возможным "поделиться" фотографией через приложения (whatsap, vk итп).
Ведь можно это сделать используя UIL?
Здравствуйте. Я пробую использовать вашу библиотеку для загрузки картинок постов из интернета. На экране прокручиваются картинки на всю ширину экрана. Максимум одновременно пользователь видит 3-4 картинки при прокрутке. Подгрузка картинок в ленту бесконечная. По сравнению с другими библиотеками(например, Picasso) ваша намного шустрее работает. Но возможно я что-то не так делаю, так как у меня возникают проблемы с памятью при загрузке большого количества картинок.
ReplyDeleteЯ пробовал разные библиотеки, но с ними тоже возникает проблема памяти. Я пробовал выводить картинки разными способами. Сначала я просто подгружал через инфлатер посты в определённый контейнер. Затем я попробовал переделать всё с нуля и реализовал через RecyclerView и адаптер. А теперь я вообще для чистоты эксперимента упростил до примитива - теперь у меня просто на экран только лишь картинки выводятся. И всё равно возникает проблема с памятью.
Т.е. проблема в том, что после определённой картинки (где-то 130) дальше картинки перестают грузиться и везде отображается картинка ошибки. В логе пишет, что OutOfMemory. Я тестирую на версии андроида 4.1. А на 2.3 ещё меньше картинок удаётся загрузить. На андроид 5 вроде бы этой проблемы не наблюдается.
Я думал подобные библиотеки как раз нужны чтобы следить за тем, чтобы картинки, которые не видны на экране выгружались из памяти. На Cyberforum мне сказали, что наоборот эти библиотеки не предназначены для этого и надо самому писать реализацию выгрузки из памяти.
Вот сам код: https://dl.dropboxusercontent.com/u/29604742/sites/HomeActivity.java
Помогите пожалуйста, а то я в тупике!
This comment has been removed by the author.
ReplyDelete