Найти в Дзене
Не только про IT

Handler, Looper, MessageQueue, Message в Android

Нюансы работы с главным потоком (UI-потоком) Когда запускается Android приложение создается главный поток (UI поток), который живет пока живет это приложение. Он обрабатывает разные события приложения, такие как нажатие на кнопку, изменения ui (изменение цвета текста, например), обработка широковещательных сообщений (Broadcast receiver) и др. Если некоторые действия выполняются продолжительно или могут тормозить приложение, то мы их должны совершать в другом потоке, т.к иначе торможение приложение может вызвать ANR (Application Not Responding) через примерно 5 секунд и вылететь. Это ошибка возникает: Если мы запускаем новый поток и там нам необходимо изменить UI, то мы должны использовать средства, которые имеют доступ к UI-потоку, т.к изменить UI может только UI-поток. MessageQueue и Message MessageQueue - это такой класс, который представляет из себя очередь объектов Message. Он работает по принципу FIFO (First-In-First-Out). Что же такое Message? Это тоже класс, который мы также мож
Оглавление

Нюансы работы с главным потоком (UI-потоком)

Когда запускается Android приложение создается главный поток (UI поток), который живет пока живет это приложение. Он обрабатывает разные события приложения, такие как нажатие на кнопку, изменения ui (изменение цвета текста, например), обработка широковещательных сообщений (Broadcast receiver) и др.

Если некоторые действия выполняются продолжительно или могут тормозить приложение, то мы их должны совершать в другом потоке, т.к иначе торможение приложение может вызвать ANR (Application Not Responding) через примерно 5 секунд и вылететь. Это ошибка возникает:

  • Когда какие-либо нажатия кнопок или сенсорные события не обрабатываются в течение 5 секунд
  • BroadcastReceiver не был обработан в течение указанного времени (foreground — 10 с, background — 60 с)
  • ContentProvider не завершен в течение 10 секунд

Если мы запускаем новый поток и там нам необходимо изменить UI, то мы должны использовать средства, которые имеют доступ к UI-потоку, т.к изменить UI может только UI-поток.

MessageQueue и Message

MessageQueue - это такой класс, который представляет из себя очередь объектов Message. Он работает по принципу FIFO (First-In-First-Out). Что же такое Message? Это тоже класс, который мы также можем создать и который имеет множество полей и методов. Основные его методы и свойства:

  • Callback - это такой Runnable, в котором и содержится то действие, которое мы хотим выполнить
  • Next - это ссылка на следующий Message (это и организует структуру MessageQueue)
  • Time - время, когда Callback должен выполниться

Если представлять MessageQueue визуально, то он выглядит примерно так:

-2

Как можно видеть, MessageQueue — это простой связный список. По сути, внутри него находится только одно поле типа Message (это первое "сообщение"), а дальше уже у него есть ссылка на следующий Message и т.д. И просто проходя по Next-ссылкам можно пройти всю очередь MessageQueue.

Все сообщения в очереди отсортированы по возврастанию поля Time. И если мы добавляем туда новый Message, то он добавится не в конец, а в соответстующее место, учитывая значение Time и сохраняя при этом последовательность. Добавление идет путем перепривязывания соседних ссылок (как в связном списке).

Looper

Давайте представим себе бесконечный цикл, который просто берет Message из MessageQueue и обрабатывает его. В этом и есть суть Looper.

Looper - это класс, предназначенный для обработки Message в MessageQueue. У него есть метод loop(), который и запускает этот бесконечный цикл и если там появилось новое сообщение, то оно его обработает.

Looper для потока можно создать при помощи метода Looper.prepare(). У главного потока Android-приложения он тоже есть. У каждого потока есть свой Looper и только один.

Метод Looper.prepare() добавляет новый объект Looper в структуру ThreadLocal<Looper>. Через метод Looper.myLooper() мы можем получить Looper для данного потока.

Для завершения работы с ним мы можем использовать метод quitSafely(), который завершит работу указанного Looper и который гарантирует, что все Message из MessageQueue для текущего потока этот Looper обработал.

Handler и HandlerThread

С его помощью мы можем связывать наш главный поток с другими потоками. Handler предназначен для управления потоками в приложении. Она позволяет отправить Message через MessageQueue в Looper.

Для того, чтобы работать с ним, нам нужно получить Looper. Чтобы получить MainLooper используют Looper.getMainLooper(). При его создании он по умолчанию привязывается к потоку и его MessageQueue.

У каждого потока может быть только один Looper, но также может быть сколько угодно. Его задача, напомню, отправлять сообщения в Looper.

Чтобы понять работу Handler, нужны еще 2 понятия:

  • Consumer-thread - это поток, который вызвал метод loop() и выполняет Callback из Message
  • Producer-thread - это поток, который отправляет Message через Handler в Consumer-thread

Визуально, это выглядит примерно так:

-3

HandlerThread - это класс, который наследуется от Thread и уже включает в себя Looper, MessageQueue и готов к созданию потока.