Я проверил официальный Android документации / руководство для Looper
, Handler
и MessageQueue
. Но я не мог этого понять. Я новичок в Android и очень запутался в этих концепциях.
Ответы:
A Looper
- это цикл обработки сообщений: он читает и обрабатывает элементы из MessageQueue
. Looper
Класс, как правило , используется в сочетании с HandlerThread
(подкласс Thread
).
A Handler
- это служебный класс, который облегчает взаимодействие с a - в Looper
основном путем отправки сообщений и Runnable
объектов в поток MessageQueue
. Когда Handler
создается, он привязывается к определенному Looper
(и связанному потоку и очереди сообщений).
При типичном использовании вы создаете и запускаете, а HandlerThread
затем создаете Handler
объект (или объекты), с помощью которого другие потоки могут взаимодействовать с HandlerThread
экземпляром. Handler
Должны быть созданы во время работы на HandlerThread
, хотя после создания не существует никаких ограничений на то , что потоки могут использовать Handler
«ы методы планирования ( post(Runnable)
и т.д.)
Основной поток (он же поток пользовательского интерфейса) в приложении Android настраивается как поток-обработчик до создания экземпляра вашего приложения.
Помимо класса Docs, есть хорошая дискуссия все это здесь .
PS Все вышеперечисленные классы находятся в пакете android.os
.
MessageQueue
государства , что MessageQueue
является « класс низкого уровня , удерживающий список сообщений , которые будут Отправляются Looper
. »
Широко известно, что в Android незаконно обновлять компоненты пользовательского интерфейса непосредственно из потоков, отличных от основного потока . Этот документ Android ( Обработка дорогостоящих операций в потоке пользовательского интерфейса ) предлагает шаги, которые необходимо выполнить, если нам нужно запустить отдельный поток для выполнения некоторых дорогостоящих работ и обновить пользовательский интерфейс после того, как это будет сделано. Идея состоит в том, чтобы создать объект Handler, связанный с основным потоком , и опубликовать в нем Runnable в соответствующее время. Это Runnable
будет вызвано в основном потоке . Этот механизм реализован с помощью классов Looper и Handler .
Looper
Класс поддерживает MessageQueue , который содержит список сообщений . Важным признаком Looper является то, что он связан с потоком, в котором Looper
создается . Эта ассоциация сохраняется навсегда и не может быть нарушена или изменена. Также обратите внимание, что поток не может быть связан более чем с одним Looper
. Чтобы гарантировать эту связь, Looper
она хранится в локальном хранилище потока и не может быть создана напрямую через конструктор. Единственный способ создать его - вызвать статический метод подготовкиLooper
. Метод подготовки сначала исследует ThreadLocalтекущего потока, чтобы убедиться, что с потоком еще не связан Looper. После обследования создается новый Looper
и сохраняется в формате ThreadLocal
. Подготовив Looper
, мы можем вызвать для него метод цикла, чтобы проверить наличие новых сообщений и Handler
разобраться с ними.
Как видно из названия, Handler
класс в основном отвечает за обработку (добавление, удаление, отправку) сообщений текущего потока MessageQueue
. Handler
Экземпляр также связан с резьбой. Связывания между обработчиком и нити достигается с помощью Looper
и MessageQueue
. Handler
Будет всегда привязан кLooper
, и впоследствии связывается с нитью , связанной с Looper
. В отличие от этого Looper
, несколько экземпляров Handler могут быть привязаны к одному потоку. Каждый раз, когда мы вызываем post или другие подобные методы в Handler
, новое сообщение добавляется в связанный MessageQueue
. В поле назначения сообщения устанавливается текущий Handler
экземпляр. КогдаLooper
Получив это сообщение, он вызывает dispatchMessage в целевом поле сообщения, чтобы сообщение возвращалось к экземпляру Handler для обработки, но в правильном потоке. Отношения между Looper
, Handler
и MessageQueue
как показано ниже:
Начнем с лупера. Вы сможете легче понять взаимосвязь между Looper, Handler и MessageQueue, если поймете, что такое Looper. Также вы можете лучше понять, что такое Looper в контексте графического интерфейса. Looper предназначен для двух вещей.
1) Looper преобразует обычный поток , который завершается, когда его run()
метод возвращается, во что-то, что работает непрерывно, пока не будет запущено приложение Android , что необходимо в среде графического интерфейса (технически, он все еще завершается, когда run()
метод возвращается. Но позвольте мне пояснить, что я имею в виду, ниже).
2) Looper предоставляет очередь, в которой задачи, которые необходимо выполнить, ставятся в очередь, что также необходимо в среде графического интерфейса.
Как вы, возможно, знаете, когда приложение запускается, система создает поток выполнения для приложения, называемый «основным», и приложения Android обычно выполняются полностью в одном потоке, по умолчанию это «основной поток». Но главный поток - это не какой-то секрет, особый поток . Это просто обычный поток, который вы также можете создать с помощью new Thread()
кода, что означает, что он завершается, когда run()
возвращается его метод! Подумайте о приведенном ниже примере.
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
Теперь давайте применим этот простой принцип к Android-приложению. Что произойдет, если приложение Android будет запущено в обычном потоке? Поток под названием «основной» или «UI» или что-то еще запускает приложение и рисует весь UI. Итак, пользователям открывается первый экран. И что теперь? Основной поток завершается? Нет, не должно. Он должен подождать, пока пользователи что-то сделают, верно? Но как добиться такого поведения? Что ж, мы можем попробовать с Object.wait()
илиThread.sleep()
. Например, основной поток завершает свою начальную работу по отображению первого экрана и засыпает. Он просыпается, что означает прерывание, когда выбирается новое задание. Пока все хорошо, но на данный момент нам нужна структура данных, подобная очереди, для хранения нескольких заданий. Подумайте о случае, когда пользователь последовательно касается экрана, и задача занимает больше времени для завершения. Итак, нам нужна структура данных, чтобы удерживать задания, которые должны выполняться в порядке очереди. Кроме того, вы можете себе представить, что реализация потока с постоянным запуском и обработкой задания по прибытии с использованием прерывания непроста и приводит к сложному и часто не поддерживаемому коду. Мы бы предпочли создать новый механизм для этой цели, и в этом вся суть Looper . Официальный документ класса Looperговорит: «По умолчанию потоки не имеют связанного с ними цикла сообщений», а Looper - это класс, «используемый для запуска цикла сообщений для потока». Теперь вы можете понять, что это значит.
Перейдем к Handler и MessageQueue. Во-первых, MessageQueue - это очередь, о которой я упоминал выше. Он находится внутри Looper, вот и все. Вы можете проверить это с помощью исходного кода класса Looper . Класс Looper имеет переменную-член MessageQueue.
Тогда что такое Хэндлер? Если есть очередь, то должен быть метод, который позволит нам поставить новую задачу в очередь, верно? Это то, что делает Хэндлер. Мы можем поставить новую задачу в очередь (MessageQueue), используя различные post(Runnable r)
методы. Вот и все. Это все о Looper, Handler и MessageQueue.
Мое последнее слово: в основном Looper - это класс, созданный для решения проблемы, возникающей в среде графического интерфейса. Но такого рода потребности могут возникнуть и в других ситуациях. На самом деле это довольно известный шаблон для многопоточного приложения, и вы можете узнать о нем больше в «Параллельном программировании на Java» Дуга Ли (особенно будет полезна глава 4.1.4 «Рабочие потоки»). Кроме того, вы можете себе представить, что этот вид механизма не является уникальным в структуре Android, но для всех платформ графического интерфейса может потребоваться что-то подобное. Вы можете найти почти такой же механизм в Java Swing framework.
MessageQueue
: Это низкоуровневый класс, содержащий список сообщений, отправляемых a Looper
. Сообщения добавляются не напрямую в a MessageQueue
, а через Handler
объекты, связанные с Looper
. [ 3 ]
Looper
: Он перебирает a, MessageQueue
который содержит сообщения для отправки. Фактическая задача управления очередью выполняется агентом, Handler
который отвечает за обработку (добавление, удаление, отправку) сообщений в очереди сообщений. [ 2 ]
Handler
: Это позволяет передавать и обрабатывать Message
и Runnable
объекты , связанные с потока MessageQueue
. Каждый экземпляр Handler связан с одним потоком и очередью сообщений этого потока. [ 4 ]
Когда вы создаете новый Handler
, он привязывается к потоку / очереди сообщений потока, который его создает - с этого момента он будет доставлять сообщения и запускаемые файлы в эту очередь сообщений и выполнять их по мере выхода из очереди сообщений. .
Пожалуйста, просмотрите изображение ниже [ 2 ] для лучшего понимания.
Расширяя ответ @K_Anas, с примером, как указано
Широко известно, что в Android незаконно обновлять компоненты пользовательского интерфейса непосредственно из потоков, кроме основного потока.
например, если вы попытаетесь обновить пользовательский интерфейс с помощью Thread.
int count = 0;
new Thread(new Runnable(){
@Override
public void run() {
try {
while(true) {
sleep(1000);
count++;
textView.setText(String.valueOf(count));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start();
ваше приложение выйдет из строя за исключением.
android.view.ViewRoot $ CalledFromWrongThreadException: только исходный поток, создавший иерархию представлений, может касаться его представлений.
другими словами, вам нужно использовать Handler
который сохраняет ссылку на MainLooper
ie Main Thread
или UI Thread
и передает задачу как Runnable
.
Handler handler = new Handler(getApplicationContext().getMainLooper);
int count = 0;
new Thread(new Runnable(){
@Override
public void run() {
try {
while(true) {
sleep(1000);
count++;
handler.post(new Runnable() {
@Override
public void run() {
textView.setText(String.valueOf(count));
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start() ;