Обработка изображений в отдельном потоке

Методы BitmapFactory.decode*, которые мы обсудили в предыдущем уроке, не должны исполняться в основном потоке пользовательского интерфейса, если данные читаются с диска или загружаются из сети (или из любых других источников, кроме памяти). Нельзя предугадать сколько времени уйдет на загрузку данных, это зависит от различных факторов (скорость чтения с диска, скорость загрузки по сети, размер изображения, мощность процессора, и.т.д). Если такая задача заблокирует поток пользовательского интерфейса, система отметит ваше приложение как зависшее, и пользователь может закрыть его (читайте подробности в разделе Сохранение отзывчивости приложения).

В этом уроке мы расскажем как обрабатывать изображения в отдельном потоке используя класс AsyncTask и решать вопросы параллельной работы.

Использование AsyncTask

Класс AsyncTask позволяет легко запустить код в отдельном потоке и возвращать результат работы в основной поток пользовательского интерфейса. Чтобы использовать его, создайте подкласс и перезагрузите нужные методы. Ниже приведен пример загрузки большого изображения в компонент ImageView с использованием AsyncTask и метода decodeSampledBitmapFromResource():

WeakReference используется для того, чтобы AsyncTask не влиял на работу сборщика мусора с объектом . Но он не гарантирует, что ImageView существует после завершения операции, поэтому вы должны проверять ссылку на объект в методе onPostExecute(). Объект ImageView может не существовать, если, например, пользователь покинет явление или по каким-то причинам изменится конфигурация разметки до того, как операция закончится.

Чтобы запустить асинхронную загрузку изображения, просто создайте новую задачу и выполните ее:

Управление параллельной работой

В предыдущем разделе урока мы обратили внимание на то, что такие компоненты интерфейса, как ListView и GridView, создают дополнительные проблемы при работе с AsyncTask. Чтобы эффективно работать с памятью, эти компоненты удаляют дочерние элементы, вроде полос прокрутки. Если каждый дочерний элемент вызывает AsyncTask, нет никакой гарантии, что когда задача завершится, связанный с ней элемент еще не будет удален, чтобы использоваться в другом компоненте. Более того, нет никакой гарантии, что асинхронные задачи завершаются в таком же порядке, в каком вы их запускаете.

В материале Мультипоточность и производительность обсуждается параллельная работа и предлагается решение, в котором ImageView хранит ссылку на последний AsyncTask, который может быть проверен после завершения задачи. Мы может использовать предыдущий пример и последовать аналогичной схеме.

Создайте подкласс класса Drawable, для хранения ссылки на рабочее задание. В данном примере, используется класс BitmapDrawable, чтобы картинка могла отображаться в компоненте ImageView, после выполнения задачи:

Прежде чем выполнить BitmapWorkerTask, создадим объект AsyncDrawable и свяжем его с ImageView:

Метод cancelPotentialWork в примере выше, проверяет наличие другой запущенной задачи, которая уже ассоциирована с ImageView. Если это так, он пытается отменить предыдущую задачу с помощью метода cancel(). В редких случаях данные новой задачи соответствуют данным существующих задач. В следующем примере приведен код метода cancelPotentialWork:

Метод-помощник getBitmapWorkerTask(), используемый в предыдущем примере, нужен для получения задачи, ассоциированной с ImageView:

Последним шагом обновим метод onPostExecute() в классе BitmapWorkerTask. Он будет проверять отменена ли задача и ассоциирована ли текущая задача с ImageView:

Такая реализация подходит для использования с компонентами ListView и GridView, а также с другими компонентами, которые удаляют дочерние элементы. Просто вызовите метод loadBitmap в том месте, где вы обычно устанавливаете картинку в ImageView. Например, при работе с GridView его надо вызывать в методе getView() адаптера.

Добавить комментарий