Полный список
— создаем более содержательные сообщения для Handler
В прошлых уроках мы использовали метод sendEmptyMessage. Этот метод сам создавал сообщение Message, заполнял его атрибут what и отправлял в очередь. Кроме what у сообщения есть еще атрибуты arg1 и arg2 типа int, и obj типа Object. В этом уроке мы сами будем создавать сообщение, заполнять атрибуты и отправлять.
Создадим приложение, которое будет подключаться к серверу, запрашивать кол-во файлов готовых для загрузки, эмулировать загрузку и отображать на экране ход действий, используя горизонтальный ProgressBar и TextView.
Project name: P0821_HandlerAdvMessage
Build Target: Android 2.3.3
Application name: HandlerAdvMessage
Package name: ru.startandroid.develop.p0821handleradvmessage
Create Activity: MainActivity
В onCreate мы создаем Handler и в его методе обработки (handleMessage) прописываем всю логику изменения экрана в зависимости от приходящих сообщений. Не буду подробно это расписывать, там все просто – меняем текст, включаем/выключаем кнопку, показываем/скрываем ProgressBar, меняем значение ProgressBar. Из интересного здесь стоит отметить, что читаем мы на этот раз не только what, но и остальные атрибуты сообщения – arg1, arg2, obj. А как они заполняются, увидим далее.
В onclick создаем новый поток для загрузки файлов. Устанавливаем подключение, получаем кол-во готовых для загрузки файлов. Если файлов для загрузки нет, посылаем соответствующее сообщение в Handler и отключаемся. Если же файлы есть, мы создаем сообщение Message с помощью метода obtainMessage (int what, int arg1, int arg2). Он принимает на вход атрибуты what, arg1 и arg2. В what мы кладем статус, в arg1 — кол-во файлов, arg2 – не нужен, там просто ноль.
Далее начинаем загрузку. После загрузки каждого файла мы создаем сообщение Message c помощью метода obtainMessage (int what, int arg1, int arg2, Object obj), заполняем его атрибуты: what – статус, arg1 – порядковый номер файла, arg2 – кол-во оставшихся файлов, obj – файл. И отправляем.
По завершению загрузки отправляем соответствующее сообщение и отключаемся.
downloadFile – эмулирует загрузку файла. ждет две секунды и возвращает массив из 1024 байтов.
saveFile – метод сохранения файла на диск. Просто заглушка. Ничего не делает.
Все сохраняем и запускаем. Жмем Connect.
Далее, либо начинается загрузка
либо появляется сообщение, что файлов нет
Используя разные атрибуты кроме what, мы смогли передать в основной поток и использовать там более разнообразные данные.
Мы создаем сообщения с помощью разных реализаций метода obtainMessage. А почему бы не создавать напрямую объект Message с помощью его конструкторов? В принципе можно, но официальный хелп рекомендует пользоваться методами obtainMessage, потому что это эффективней и быстрее. В этом случае сообщение достается из глобального пула сообщений, а не создается с нуля.
Здесь вы можете посмотреть все реализации метода obtainMessage для формирования сообщений и использовать тот, который подходит для ситуации. Они различаются разными комбинациями входных параметров.
На следующем уроке:
— посылаем отложенные сообщения
— удаляем сообщения из очереди
— используем Handler.Callback для обработки сообщений
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Полный список
— посылаем простейшее сообщение для Handler
Надеюсь, вы прониклись предыдущим уроком и осознали, какая полезная штука Handler. Мы там отправляли ему сообщение. Сегодня сделаем это еще раз, но уже без кучи лишнего кода, зависающих экранов и ошибок приложения. Этакий чистый пример, чтобы закрепить.
Как мы помним, Handler позволяет класть в очередь сообщения и сам же умеет их обрабатывать. Фишка тут в том, что положить сообщение он может из одного потока, а прочесть из другого.
Сообщение может содержать в себе атрибуты. Сегодня рассмотрим самый простой вариант, атрибут what.
Напишем простое приложение-клиент. Оно, как-будто, будет подключаться к серверу, выполнять какую-то работу и отключаться. На экране мы будем наблюдать, как меняется статус подключения и как крутится ProgressBar при подключении.
При сменах состояния подключения мы будем отправлять сообщение для Handler. А в атрибут what будем класть текущий статус. Handler при обработке сообщения прочтет из него what и выполнит какие-либо действия.
Project name: P0811_ HandlerSimpleMessage
Build Target: Android 2.3.3
Application name: HandlerSimpleMessage
Package name: ru.startandroid.develop.p0811handlersimplemessage
Create Activity: MainActivity
Кнопка для старта подключения, TextView для вывода информации о статусе подключения и ProgressBar, работающий в процессе подключения.
STATUS_NONE, STATUS_CONNECTING, STATUS_CONNECTED – это константы статуса. Их будем передавать в сообщении, в атрибуте what. Разумеется, названия и значения этих констант произвольны и взяты из головы. Вы можете придумать и использовать свои.
В onCreate мы создаем Handler и реализуем его метод handleMessage. Этот метод отвечает за обработку сообщений, которые предназначены для этого Handler. Соответственно на вход метода идет сообщение – Message. Мы читаем атрибут what и в зависимости от статуса подключения меняем экран:
STATUS_NONE – нет подключения. Кнопка подключения активна, TextView отражает статус подключения.
STATUS_CONNECTING – в процессе подключения. Кнопка подключения неактивна, показываем ProgressBar, TextView отражает статус подключения.
STATUS_CONNECTED – подключено. Скрываем ProgressBar, TextView отражает статус подключения.
В onCreate после создания Handler мы сразу отправляем ему сообщение со статусом STATUS_NONE. Для этого мы используем метод sendEmptyMessage. В этом методе создается сообщение, заполняется его атрибут what (значением, которое мы передаем в sendEmptyMessage), устанавливается Handler в качестве адресата и сообщение отправляется в очередь.
В методе onclick мы создаем и запускаем новый поток. В нем мы, с помощью sleep, эмулируем процесс подключения к серверу, выполнение работы и отключение. И, по мере выполнения действий, отправляем сообщения со статусами для Handler. Т.е. получается, что после нажатия на кнопку Connect статус меняется на STATUS_CONNECTING, две секунды идет подключение, статус меняется на STATUS_CONNECTED, 3 секунды выполняются действия и статус меняется на STATUS_NONE. Давайте проверим.
Все сохраним и запустим приложение.
Подключение пошло. Появляется ProgressBar, меняется текст и неактивна кнопка Connect.
Проходит две секунды.
Подключение установлено. ProgressBar исчезает и меняется текст.
Проходит еще 3 секунды.
Подключение завершается. Кнопка Connect снова активна, а текст показывает, что мы не подключены.
Т.е. для простого обновления статуса из нового потока нам хватило атрибута what. Но кроме what сообщение может иметь еще несколько атрибутов. Рассмотрим их на следующем уроке.
На следующем уроке:
— создаем более содержательные сообщения для Handler
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Android UI thread
Большая часть кода Android приложения работает в контексте компонент, таких как Activity, Service, ContentProvider или BroadcastReceiver. Рассмотрим, как в системе Android организованно взаимодействие этих компонент с потоками.
При запуске приложения система выполняет ряд операций: создаёт процесс ОС с именем, совпадающим с наименованием пакета приложения, присваивает созданному процессу уникальный идентификатор пользователя, который по сути является именем пользователя в ОС Linux. Затем система запускает Dalvik VM где создаётся главный поток приложения, называемый также «поток пользовательского интерфейса (UI thread)». В этом потоке выполняются все четыре компонента Android приложения: Activity, Service, ContentProvider, BroadcastReceiver. Выполнение кода в потоке пользовательского интерфейса организованно посредством «цикла обработки событий» и очереди сообщений.
Рассмотрим взаимодействие системы Android с компонентами приложения.
Activity. Когда пользователь выбирает пункт меню или нажимает на экранную кнопку, система оформит это действие как сообщение (Message) и поместит его в очередь потока пользовательского интерфейса (UI thread).
Service. Исходя из наименования, многие ошибочно полагают, что служба (Service) работает в отдельном потоке (Thread). На самом деле, служба работает так же, как Activity в потоке пользовательского интерфейса. При запуске локальной службы командой startService, новое сообщение помещается в очередь основного потока, который выпонит код сервиса.
BroadcastReceiver. При создании широковещательного сообщения система помещает его в очередь главного потока приложения. Главный поток позднее загрузит код BroadcastReceiver который зарегистрирован для данного типа сообщения, и начнёт его выполнение.
ContentProvider. Вызов локального ContentProvider немного отличается. ContentProvider также выполняется в основном потоке но его вызов является синхронным и для запуска кода ContentProvider не использует очередь сообщений.
Исходя из вышесказанного можно заметить, что если главный поток в данный момент обрабатывает пользовательский ввод или выполняет иное действие, выполнение кода, полученного в новом сообщении, начнётся только после завершения текущей операции. Если какая либо операция в одном из компонентов потребует значительного времени выполнения, пользователь столкнётся или с анимацией с рывками, или с неотзывчивыми элементами интерфейса или с сообщением системы «Приложение не отвечает» (ANR).
Для решения данной проблемы используется парадигма параллельного программирования. В Java для её реализации используется понятие потока выполнения (Thread).
Thread: поток, поток выполнения, иногда ещё упоминается как нить, можно рассматривать как отдельную задачу, в которой выполняется независимый набор инструкций. Если в вашей системе только один процессор то потоки выполняются поочередно (но быстрое переключение системы между ними создает впечатление параллельной или одновременной работы). На диаграмме показано приложение, которое имеет три потока выполнения:
Но, к сожалению, для взаимодействия с пользователем, от потока мало пользы. На самом деле, если вы внимательно посмотрите на диаграмму выше, вы поймёте, что как только поток выполнить все входящие в него инструкции он останавливается и перестаёт отслеживать действия пользователя. Чтобы избежать этого, нужно в наборе инструкций реализовать бесконечный цикл. Но возникает проблема как выполнить некое действие, например отобразить что-то на экране из другого потока, иными словами как вклиниться в бесконечный цикл. Для этого в Android можно использовать Android Message System. Она состоит из следующих частей:
Looper: который ещё иногда ещё называют «цикл обработки событий» используется для реализации бесконечного цикла который может получать задания используется. Класс Looper позволяет подготовить Thread для обработки повторяющихся действий. Такой Thread, как показано на рисунке ниже, часто называют Looper Thread. Главный поток Android на самом деле Looper Thread. Looper уникальны для каждого потока, это реализованно в виде шаблона проектирования TLS или Thread Local Storage (любопытные могут посмотреть на класс ThreadLocal в Java документации или Android).
Message: сообщение представляет собой контейнер для набора инструкций которые будут выполнены в другом потоке.
Handler: данный класс обеспечивает взаимодействие с Looper Thread. Именно с помощью Handler можно будет отправить Message с реализованным Runnable в Looper, которая будет выполнена (сразу или в заданное время) потоком с которым связан Handler. Код ниже иллюстрирует использование Handler. Этот код создаёт Activity которая завершиться через определённый период времени.
HandlerThread: написание кода потока реализующего Looper может оказаться не простой задачей, чтобы не повторять одни и те же ошибки система Android включает в себя класс HandlerThread. Вопреки названию этот класс не занимается связью Handler и Looper.
Практическую реализацию данного подхода можно изучить на примере кода класса IntentService, данный класс хорошо подходит для выполнения асинхронных сетевых или иных запросов, так как он может принимать задания одно за другим, не дожидаясь полной обработки текущего, и завершает свою работу самостоятельно.
Выполнение операций в отдельном потоке, не означает, что вы можете делать все что угодно, не влияя на производительность системы. Никогда не забывайте, что написанный вами код работает на машинах, как правило, не очень мощных. Поэтому всегда стоит использовать возможности предоставляемые системой для оптимизации.
Подготовлено на основе материалов AndroidDevBlog
Источник