Русские Блоги
Краткое описание Handler
Handler
Во-первых, что такое Handler
* Обработчик находится в SDKОбработка асинхронных сообщенийОсновнойучебный класс。
в основном принимает данные, отправленные дочерним потоком, и использует эти данные для обновления пользовательского интерфейса с помощью основного потока.
Роль: может использоваться дляОбновить интерфейс (Но не только этот) *
Обработчик может не только завершить взаимодействие между основным потоком и дочерним потоком, но также может обеспечить связь между дочерним потоком и дочерним потоком.
Во-вторых, зачем нужна ручка
потому чтоДочерний поток не может обновить пользовательский интерфейс, Только поток пользовательского интерфейса (то есть основной поток) может обновлять пользовательский интерфейс. Возможно, некоторые люди говорят, что, поскольку дочерний поток не работает, мы поместим эту операцию изменения вОсновной потокВнутри. (Примечание: основной поток: поток пользовательского интерфейса)
иВ Android есть правила, которые не могут отнимать много времени в основном потоке.Следовательно, в дочернем потоке будут выполняться трудоемкие операции, а затем будет отправлен запрос, а основной поток обновит пользовательский интерфейс, поэтому будет представлен обработчик.
В-третьих, краткое описание механизма работы Handler.
1.Message Под сообщением понимается информационная единица связи между потоками. Например, если фоновый подпоток должен обновить пользовательский интерфейс после обработки данных, он может отправить сообщение, содержащее информацию об обновлении, в поток пользовательского интерфейса.
4.Looper Циркулятор, действующий как мост между очередью сообщений и обработчиком, циклически извлекает сообщение из очереди сообщений и доставляет его соответствующему обработчику для обработки.
1.В Activity есть только один основной поток (т. Е. Поток пользовательского интерфейса).
2.Поток пользовательского интерфейса автоматически создаст MessageQueue и Looper.
3.Будет только один MessageQueue и один Looper.
Четыре, MessageQueue
MessageQueue используется для хранения коллекции сообщений, а экземпляр Looper распределяет объекты сообщений внутри. В то же время сообщение не добавляется напрямую в MessageQueue, а завершается объектом MessageQueue.IdleHandler, связанным с объектом Looper. Мы можем получить MessageQueue текущего потока с помощью метода Looper.myQueue ().
Пять, лупер
6. Зачем использовать handlerMessageQueue, Looper?
Основной поток не может выполнять долгосрочные задачи, поэтому ему нужен подпоток для его обработки, но подпоток не может обновлять интерфейс пользовательского интерфейса, поэтому нам нужно использовать обработчик для передачи сообщений в основной поток для завершения пользовательского интерфейса. Обновить. Поскольку основной поток и дочерний поток работают в разное время, необходимо использовать MessageQueue для хранения сообщения дочернего потока, а Looper передает сообщение в основной поток для ответа.
Семь основных шагов по использованию обработчика
Основные этапы делятся на три этапа:
Сначала создайте обработчик.
Отправить сообщение из дочерней цепочки
Захватите необходимое сообщение в обработчике и реализуйте ответ
Обработать фактическую операцию можно по ссылке ниже
Исходная ссылка:https://blog.csdn.net/qq_38845493/article/details/80609509
Интеллектуальная рекомендация
JDBC Review.
2. Запрос всех студентов, которые избрали курс № 1
UIImageView
Многопоточность в Android. Looper, Handler, HandlerThread. Часть 1.
Что вы знаете о многопоточности в андроид? Вы скажете: «Я могу использовать AsynchTask для выполнения задач в бэкграунде». Отлично, это популярный ответ, но что ещё? «О, я слышал что-то о Handler’ах, и даже как то приходилось их использовать для вывода Toast’ов или для выполнения задач с задержкой…» — добавите Вы. Это уже гораздо лучше, и в этой статье мы рассмотрим как и для чего используют многопоточность в Android.
Для начала давайте взглянем на хорошо известный нам класс AsyncTask, я уверен что каждый андроид-разработчик использовал его. Прежде всего, стоит заметить, что есть отличное описание этого класса в официальной документации. Это хороший и удобный класс для управления задачами в фоне, он подойдёт для выполнения простых задач, если вы не хотите тратить впустую время на изучение того как можно эффективно управлять потоками в андроид. Самая главная вещь о которой вы должны знать – только метод doInBackground выполняется в другом потоке! Остальные его методы выполняются в главном UI потоке. Рассмотрим пример типичного использования AsyncTask:
Далее в интерфейсе будем использовать следующий тривиальный макет с прогресбаром для всех наших тестов.
В макете бесконечно отображается индикация вращения прогресбара. Если прогресбар застынет – это будет что в главном UI потоке происходит выполнение тяжелой работы.
Здесь мы используем AsyncTask, потому что приложению потребуется некоторое время для получения ответа от сервера и мы не хотим что бы нам интерфейс завис в ожидании этого ответа, поэтому мы поручаем выполнить сетевую задачу другому потоку. Есть много постов о том, почему использование AsyncTask – это плохая затея(например: если это внутренний класс вашего активити/фрагмента, то он будет удерживать внутреннюю ссылку на него, что является плохой практикой, потому что активити/фрагмент могут быть уничтожены при смене конфигурации, но они будут висеть в памяти пока работает фоновый поток; если объявлен отдельным статическим внутренним классом и вы используете ссылку на Context для обновления вьюшек, вы должны всегда проверять их на null).
Все задачи в главном потоке выполняются последовательно, делая тем самым код более предсказуемым – вы не рискуете попасть в ситуацию изменения данных несколькими потоками. Значит если какая-то задача работает слишком долго, Вы получите неотвечающее приложени, или ANR(Application Not Responding) ошибку. AsyncTask является одноразовым решением. Класс не может быть повторно использован при повторном вызове execute метода на одном экземпляре – вы непременно должны создать новый экземпляр AsyncTask для новой работы.
Любопытно то, что если вы попытаетесь показать Toast из метода doInBackground, то получите ошибку, содержащую что то вроде:
Из-за чего же мы получили ошибку? Ответ прост: потому что Toast является частью интерфейса и может быть показан только из UI потока, а правильный ответ: потому что он может быть показан только из потока с Looper’ом! Вы спросите, что такое Looper?
Вы можете спросить: «Для чего вся эта сложность, если задачи всё равно обрабатываются их создателями – Handler‘ами?». Мы получаем как минимум 2 преимущества:
— это помогает избежать состояния гонки (race conditions), когда работа приложения становится зависимой от порядка выполнения потоков;
— Thread не может быть повторно использован после завершения работы. А если поток работает с Looper’ом, то вам не нужно повторно создавать экземпляр Thread каждый раз для работы в бэкграунде.
Вы можете сами создавать и управлять Thread’ами и Looper’ами, но я рекомендую воспользоваться HandlerThread (Google решили использовать HandlerThread вместо LooperThread) : В нём уже есть встроенный Looper и всё настроено для работы.
А что о Handler? Это класс с двумя главными функциями: отправлять задачи в очередь сообщений (MessageQueue) и выполнять их. По умолчанию Handler неявно связывается с потоком в котором он был создан с помощью Looper’a, но вы можете связать его явно с другим потоком, если предоставите ему другой Looper в конструкторе. Наконец-то пришло время собрать все куски теории вместе и взглянуть на примере как всё это работает! Представим себе Activity в которой мы хотим отправлять задачи(в моей статье задачи представлены экземплярами Runnable интерфейса, что такое на самом деле задача(или сообщение) я расскажу во второй части статьи) в очередь сообщений(все активити и фрагменты существуют в главном UI потоке), но они должны выполняться с некоторой задержкой:
Так как mUiHandler связан с главным потоком(он получил Looper главного потока в конструкторе по умолчанию) и он является членом класса, мы можем получить к нему доступ из внутреннего анонимного класса и поэтому можем отправлять задачи-сообщения в главный поток. Мы используем Thread в примере выше и не можем использовать его повторно, если хотим выполнить новую задачу. Для этого нам придется создать новый экземпляр. Есть другое решение? Да! Мы можем использовать поток с Looper’ом. Давайте немного модифицируем пример выше с целью заменить Thread на HandlerThread для демонстрации того, какой прекрасной способностью к повторному использованию он обладает:
Порой это отлично работает, но иногда вы будете выхватывать NPE после вызова
postTask, с сообщением о том, что ваш mWorkerHandler — null. Сюрприз!
Почему это произошло? Хитрость здесь в нативном вызове, необходимом для создании нового потока. Если мы взглянем на кусок кода, где вызывается onLooperPrepared, мы найдём следующий фрагмент в HandlerThread классе:
Хитрость заключается в том, что run метод будет вызван только после того, как новый поток создаётся и запускается. И этот вызов может иногда произойти после вызова postTask(можете проверить это самостоятельно, просто поместите точку останова внутри postTask и onLooperPreparerd методов и взгляните, какой из них будет первым), таким образом вы можете стать жертвой состояния гонки между двух потоков (main и background).
Во второй части разберем как на самом деле внутри MessageQueue работают задачи.
Русские Блоги
17_ Андроид коммуникационный механизм Handler
каталог
1. Что такое Handler, Looper, MessageQueue
Обработчик: это механизм для помещения сообщения в очередь. В каком потоке мы обновляем обработчик, обработчик помещает сообщение в поток, где оно находится, если только конкретный поток не указан при создании объекта обработчика. Обработчик обычно создается в Main Theread.
Looper: это эквивалентно носителю сообщений. Внутри него есть очередь сообщений, то есть MessageQueue. Все сообщения, отправленные обработчиком, попадут в эту очередь сообщений.
MessageQueue: это очередь сообщений
2. Основные принципы Handler, Looper и MessageQueue
Простой пример Обработчик, Looper и MessageQueue Отношения между:
1. Когда пользователь нажимает кнопку, создается объект сообщения, а атрибуту what объекта сообщения присваивается значение 2.
2. Затем вызовите метод sendMessage обработчика, чтобы отправить объект msg в MessageQueue.
3 После того, как в очереди сообщений есть данные, Looper удалит объект сообщения из очереди сообщений.
4. Looper найдет объект Handler, соответствующий объекту сообщения (обработчик, который генерирует объект сообщения msg ( обработчик является ссылочным типом обработчика ))
5. Looper вызовет метод handlerMessage (Message, msg) объекта Handler для обработки объекта сообщения
Создайте новое приложение и назовите его: S17_Handler1
Добавьте кнопку и элемент управления TextView в файл макета;
Объявите ссылку, получите метку элемента управления как объект, реализуйте интерфейс слушателя и привяжите слушателя к кнопке:
Реализуйте интерфейс слушателя, и в методе onClick запишите работу, которую необходимо выполнить при нажатии кнопки, а затем унаследуйте Handler, чтобы сгенерировать объект подкласса Handler.
1. При наследовании обработчика вам нужно использовать комбинацию клавиш для импорта пакета, обратите внимание на импорт правильного пакета
2. Метод handlerMessage перезаписывает обработчик:
Исходный код MainActivity.java:
Попробуйте запустить это приложение:
Может показаться немного запутанным чтение приведенного выше примера. Зачем сохранять данные в очереди сообщений через механизм Handler, а затем извлекать данные в Looper, а затем отправлять их обратно в объект Handler, чтобы решить их, так хлопотно, что Handler решил их сам Разве этого недостаточно?
Ответ заключается в связи между потоками и потоками. На самом деле, мы собираемся решить некоторые проблемы в связи потоков. После прочтения следующего, вы можете быть озадачены:
3. Поток связи через обработчик: (передача подпотока в основной поток)
Для достижения цели: нажмите кнопку «Отправить сообщение», чтобы запустить поток, оставьте этот поток на 2 секунды, чтобы изменить содержимое TextView, абстрактный пример:
Совет: Мы упоминали в предыдущем разделе, что невозможно изменить свойства пользовательского интерфейса в рабочем потоке. Нажмите для подробностей:Нажмите меня, чтобы посмотреть
На этот раз мы используем механизм связи Handler для решения этой проблемы.
Создайте новое приложение с именем: S17_Handler2
Код и вид файла макета выглядят следующим образом:
Слушатель привязан к кнопке:
Затем создайте новый поток с именем MyThread, используйте кнопку для запуска потока, подробности см. В предыдущем разделе:Нажмите меня, чтобы посмотреть
Как и выше, создайте новый обработчик в главном потоке, чтобы получить информацию об очереди сообщений объекта, а также выведите имя потока:
Общий код выглядит следующим образом:
Можно видеть, что, как мы и ожидали, сначала выведите имя потока NetWork (Worker Thread), затем выполните оператор handlerMessage, распечатайте имя потока и обнаружите, что это на самом деле основной поток (Main Thread):
Итак, пришли к выводу: с помощью метода sendMessage (), можно ли получить handleMessage (msg) из основного потока (поток Mian) или (рабочий поток), так как он может быть получен, мы можем использовать этот механизм Для обработки (рабочего потока) данных процесса это означает, что независимо от того, заблокирован ли ваш рабочий поток, пока основной поток не заблокирован, интерфейс пользовательского интерфейса может отображаться нормально. Как только рабочий поток обрабатывает данные, вы можете использовать метод sendMessage () Отправив данные в очередь сообщений, Looper может передать данные в обработку HandlerMessage (msg), а поскольку handleMessage (msg) является основным потоком, он может изменять свойства пользовательского интерфейса (такие как TextView и другие элементы управления).
Выше мы решили, что можем отправлять данные (Work Thread) в (Main Thread), затем мы можем передать строку just s (Sting) через механизм sendMessage (), а затем основной Поток принимает, и метод TextView.setText () может быть вызван в главном потоке для обратной связи с результатом, возвращенным потоком (Work Thread) в пользовательском интерфейсе.
Измените класс NetworkThread следующим образом:
Измените класс MyHandler следующим образом:
Запустите это приложение:
В первом примере я оставил немного сомнений, а затем отвечу здесь, почему это не может быть решено напрямую с помощью Handler, но с помощью косвенного решения здесь ясно, что Handler также находится в основном потоке, если он выполняется в основном потоке Он также будет блокировать основной поток, и наиболее важным моментом является то, что проблема не решается обработчиком, проблема все еще решается с помощью (рабочего потока), обработчик является только мостом связи между основным потоком и рабочим потоком.
Используйте график, чтобы провести абстрактную аналогию:
4. Поток связи через обработчик: (основной поток передает дочерний поток)
1. Создайте новый WorkThread, а затем подготовьте объекты Looper в процессе
2. Создать объект обработчика в рабочем потоке
3. Отправить сообщение в теме Mian
Создайте новое приложение и назовите его: S17_Handler3
Изменить файл макета:
Изменить программу MainActivity.java
1. Создайте новый WorkThread, а затем подготовьте объекты Looper в процессе
2. Создать объект обработчика в рабочем потоке
3. Отправить сообщение в теме Mian
Общая программа: обратите внимание, что следующие два утверждения были исправлены
Результат показан на рисунке ниже. Первое предложение доказывает, что сообщение было отправлено MainThread, а второе и третье предложения доказывают, что сообщение было получено на WorkerThread и получило сообщение, которое мы отправили:
5. Анализ исходного кода Android отношений Handler-Looper-MessageQueue-thread
Примечание: для описания анонимного внутреннего класса выше
1. Для чего нужен анонимный внутренний класс:Нажмите меня, чтобы посмотреть
2. Объяснение использования вышеуказанной программы:
Русские Блоги
Прежде чем приступить к обсуждению механизма обработки сообщений Android, давайте поговорим о некоторых основных связанных терминах.
Синхронизация связи(Синхронно): после отправки запроса клиенту,Клиент должен продолжать отправлять другие запросы после ответа сервера, Поэтому все запросы будут синхронизироваться на сервере, пока сервер не вернет запрос.
Асинхронная связь(Асинхронный): после того, как клиент отправит запрос,Отправьте следующий запрос, не дожидаясь ответа от сервера。
Так называемыйСинхронный звонокТо есть, когда вызывается функция или метод и результат не получен, вызов не возвращается, пока не будет возвращен результат.Асинхронный вызовОтносительно синхронного. После инициирования асинхронного вызоваВызываемый абонент немедленно возвращается к абоненту, Но вызывающий не может получить результат немедленно. Вызывающий уведомит вызывающего о результате обработки запроса посредством статуса, уведомления или обратного вызова после того, как фактический запрос на обработку вызова завершен.
Обработка сообщений Android имеет три основных класса: Looper, Handler и Message. На самом деле есть очередь сообщений (очередь сообщений), но MQ инкапсулирован в Looper, мы не будем иметь дело непосредственно с MQ, поэтому он не является базовым классом.
1. Класс сообщения: класс сообщения
Основная функция android.os.Message заключается в инкапсуляции сообщения, и в то же время вы можете указать форму операции для сообщения.Переменные и общие методы, определенные классом Message, следующие:
(1) public int what: переменная, используемая для определения, к какой операции принадлежит это Сообщение
(2) публичный объект obj: переменная, используемая для определения информационных данных, передаваемых этим сообщением, и передачи информации через него
(3) public int arg1: переменная, используемая при передаче некоторых целочисленных данных
(4) public int arg2: переменная, используемая при передаче некоторых целочисленных данных
(5) public Handler getTarget (): общий метод для получения объекта Handler, который оперирует этим сообщением.
Во всем механизме обработки сообщенийmessageТакже называется задачей,инкапсулируетЗадача выполненаинформацияИ справиться с задачейhandler, Использование сообщения относительно просто, но есть несколько моментов, на которые следует обратить внимание:
(1) Хотя Message имеет открытый конструктор по умолчанию, вы должны получить пустой объект сообщения из пула сообщений через Message.obtain () для экономии ресурсов.
(2) Если ваше сообщение должно нести простойintИнформация, пожалуйста, используйте Message.arg1 и Message.arg2 для передачи информации, что экономит больше памяти, чем Bundle
(3) Несанкционированное использование сообщения.whatприходИдентификационная информацияДля обработки сообщений по-разному.
2. Канал сообщений: Looper
public class LooperThread extends Thread <
// Инициализируем текущий поток в поток Looper
//. Другая обработка, например, создание экземпляра обработчика
// Начать цикл по очереди сообщений
Через две строки кода выше, ваш поток будет обновлен до потока Looper! Так что же сделали эти две строки кода?
1)Looper.prepare (): Создать объект Looper.
Как видно из рисунка выше, в вашем потоке теперь есть объект Looper, который поддерживает внутреннюю очередь сообщений MQ. Обратите внимание,Поток может иметь только один объект LooperЗачем? Посмотрите на исходный код
public class Looper <
// Объект Looper в каждом потоке на самом деле является ThreadLocal, то есть объектом локального хранилища потока (TLS)
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Очередь сообщений в Looper
final MessageQueue mQueue;
// Каждый объект Looper имеет свою очередь сообщений и поток, к которому он принадлежит
mQueue = new MessageQueue();
// Мы вызываем этот метод для создания объекта Looper в TLS вызывающего потока
public static final void prepare() <
// Попытка создать Looper снова в потоке с Looper вызовет исключение
throw new RuntimeException( «Only one Looper may be created per thread» );
sThreadLocal.set( new Looper());
Рабочий метод метода prepare () понятен, и его ядром является определение объекта петлителя как ThreadLocal.
2)Looper.loop (): цикл для получения сообщения в MQ и отправки его соответствующему объекту-обработчику.
После вызова метода цикла поток Looper начинает работать и постоянно извлекает сообщение руководителя группы (также называемое задачей) из своего MQ для выполнения. Анализ исходного кода выглядит следующим образом:
public static final void loop() <
Looper me = myLooper(); // Получить текущий поток Looper
MessageQueue queue = me.mQueue; // Получить MQ текущего петлителя
final long ident = Binder.clearCallingIdentity();
Message msg = queue.next(); // удаляем сообщение
if (msg.target == null ) <
// Сообщение не имеет цели в качестве конечного сигнала, выход из цикла
if (me.mLogging!= null ) me.mLogging.println(
«>>>>> Dispatching to » + msg.target + » «
// Очень важно! Дайте реальную работу по обработке цели сообщения, обработчик будет описан позже
msg.target.dispatchMessage(msg);
if (me.mLogging!= null ) me.mLogging.println(
final long newIdent = Binder.clearCallingIdentity();
+ Long.toHexString(ident) + » to 0x»
+ Long.toHexString(newIdent) + » while dispatching to «
+ msg.callback + » what=» + msg.what);
// перерабатывать ресурсы сообщений
В дополнение к методам prepare () и loop () класс Looper также предоставляет некоторые полезные методы, такие какLooper.myLooper()получитьТекущий объект петлителя потока:
public static final Looper myLooper() <
// Вызов Looper.myLooper () в любом потоке возвращает петлитель этого потока
return (Looper)sThreadLocal.get();
GetThread () получает поток, к которому принадлежит объект петлителя:
public Thread getThread() <
Метод quit () завершает цикл петлителя:
// Создать пустое сообщение, его целью является NULL, что означает конец цикла сообщения
Message msg = Message.obtain();
Таким образом, Looper имеет следующие моменты:
1)Каждый поток имеет один и только один объект LooperЭто ThreadLocal
2) Внутри LooperЕсть очередь сообщенийПосле вызова метода loop () поток начинает принимать сообщения из очереди и выполнять
3)Looper превращает нить в нить Looper。
Итак, как нам манипулировать сообщениями в очереди сообщений? Это использование Handler
3. Класс работы с сообщением: класс обработчика
Объект Message инкапсулирует все сообщения, и для работы этих сообщений требуется класс android.os.Handler. Что такое обработчик?handlerИгралОбработка сообщений на MQРоль (касается только сообщений, отправленных вами), а именноСообщите MQ, что он хочет выполнить задачу (sendMessage), и выполнить задачу (handleMessage) при зацикливании на себя。При создании обработчик будет связан с петлителем. Метод построения по умолчанию будет связан с петлителем текущего потока, но его также можно установить, Метод построения по умолчанию:
public class handler <
final MessageQueue mQueue; // Ассоциированный MQ
final Looper mLooper; // Связанная петляr
final Callback mCallback;
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0 ) <
Log.w(TAG, «The following Handler class should be static or leaks might occur: » + klass.getCanonicalName());
// По умолчанию петлитель текущего потока будет связан
// Looper не может быть пустым, то есть конструктор по умолчанию может использоваться только в потоке LOOPER.
throw new RuntimeException(
«Can’t create handler inside thread that has not called Looper.prepare()» );
Ниже мы можем добавить обработчик для предыдущего класса LooperThread:
public class LooperThread extends Thread <
private Handler handler1;
private Handler handler2;
// Инициализируем текущий поток в поток Looper
// Создание двух обработчиков
handler1 = new Handler();
handler2 = new Handler();
// Начать цикл по очереди сообщений
Эффект после добавления обработчика выглядит следующим образом:
Как видите,Поток может иметь несколько обработчиков, но только один Looper!
Обработчик отправляет сообщение
С обработчиком мы можем использовать
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
// Этот метод используется для отправки объекта Runnable в связанный MQ, а его метод run будет выполнен в потоке петлителей, связанном с обработчиком
public final boolean post(Runnable r)
// Обратите внимание, что getPostMessage (r) инкапсулирует runnable в сообщение
return sendMessageDelayed(getPostMessage(r), 0 );
private final Message getPostMessage(Runnable r) <
Message m = Message.obtain(); // Получить пустое сообщение
m.callback = r; // Установить runnable для обратного вызова сообщения,
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
boolean sent = false ;
MessageQueue queue = mQueue;
msg.target = this ; // Цель сообщения должна быть установлена в обработчик!
sent = queue.enqueueMessage(msg, uptimeMillis);
RuntimeException e = new RuntimeException(
this + » sendMessageAtTime() called with no mQueue» );
Сообщение, отправленное обработчиком, имеет следующие характеристики:
2. Сообщение, отправленное по почте,Его обратный вызов является объектом Runnable
Обработчик обработки сообщений
После отправки сообщения давайте посмотрим, как обработчик обрабатывает сообщение. Обработка сообщения осуществляется с помощью основного метода dispatchMessage (сообщение msg) и метода ловушки handleMessage (сообщение msg)
Завершено, см. Исходный код
Можно видеть, что, за исключением того, что методы run объектов handleMessage (Message) и Runnable реализованы разработчиками (реализуя специальную логику), внутренний рабочий механизм обработчика прозрачен для разработчиков. Обработчик имеет следующие две важные функции:
1) Обработчик может бытьОтправлять сообщения из любой темыЭти сообщения будут добавлены в связанный MQ
2)Обработка сообщения осуществляется с помощью основного метода dispatchMessage (Message msg) и метода ловушки handleMessage (Message msg).Хэндлер в немОбрабатывать сообщения в связанном потокеA.
Пример кода приведен ниже только для справки:
Android Handler механизм исследования заметки исследования(Принципиальная схема)
Android работает процесс
Для того, чтобы лучше понять механизм работы с обработчиком, мы должны прежде всего, весь процесс запуска системы Android должен быть знаком с сердцем, следующееСхема запуска процесса Android:
Мы можем видеть на картинке,Когда мы вызываем компонент извне, Service и ContentProvider получают потоки из пула потоков, тогда как Activity и BroadcastReceiver запускаются непосредственно в основном потоке., Чтобы отслеживать поток, мы можем использовать метод отладки или использовать класс инструментов, здесь мы создаем класс инструментов для мониторинга потоков
public class Utils <
public static long getThreadId()<
Thread t = Thread.currentThread();
* Получить индивидуальную информацию по теме
public static String getThreadSignature()<
Thread t = Thread.currentThread();
String name = t.getName();
long p = t.getPriority();
String gname = t.getThreadGroup().getName();
return ( «(Thread):» +name+ «:(id)» + l + «(:priority)» + p + «:(group)» + gname );
* Получить текущую информацию потока
public static void logThreadSignature()<
public static void logThreadSignature(String name )<
public static void sleepForInSecs(int secs)<
Thread. sleep (secs * 1000);
// TODO: handle exception
* Разговор о String в Bundle
public static Bundle getStringAsBundle(String message)<
Bundle b = new Bundle();
* Получить Bundle String
public static String getStringFromABundle(Bundle b)<
return b.getString( «message» );
С таким классом нам удобно наблюдать за потоком
Хорошо, теперь, когда вы будете готовы, введите тему обработчик
Handlers
Зачем использовать обработчики?
Поскольку мы являемся нашей очередью основного потока, при обработке сообщения в течение более 5 секунд, android будет выдавать сообщение ANP (не отвечает), поэтому нам нужно поместить несколько длинных сообщений для обработки в отдельный поток. Внутри обработки верните результаты после обработки в основной поток для запуска, вам нужно использоватьОбработчик для связи с потоком, отношения таковы;
НижеСвязь между обработчиком, сообщением, очередью сообщений
Эта диаграмма имеет 4 места, связанных с обработчиками
1, основная нить (Main thread)
2, очередь основного потока (очередь основного потока)
В вышеуказанных четырех местах нам не нужно иметь дело с основным потоком и очередью основного потока, поэтому мы в основном имеем дело с отношениями между обработчиком и сообщением.
Каждый раз, когда мы отправляем Сообщение, Сообщение попадает в очередь основного потока, а затем Обработчик может вызывать данные, связанные с Сообщением, для работы с компонентами основного потока.
Message
Как объект, принятый обработчиком, мы должны знать, какой тип данных является типом данных Message.
Из официального документа мы можем узнать сообщение оПоля данных
| public int what |
| public int arg1 |
| public int arg2 |
| public Object obj |
Как видно из приведенной выше таблицы, message предоставляет объект для хранения объекта, а также предоставляет три поля int для хранения небольшого числа типов int
Конечно, в дополнение к вышеупомянутым трем собственным полям сообщения, мы также можем передатьsetData(Bundle b), Для хранения объекта Bundle, для хранения более богатых типов данных, например, изображений и т. Д.
Message msg = obtainMessage();
Message msg = mHandler.obtainMessage(int what);
// То же самое верно ниже
Message msg = mHandler.obtainMessage(int what,Object object);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2, Object obj
Andriod предоставляет Handler и Looper для связи между потоками. Хендлер первым в порядке первым. Класс Looper используется для управления обменом сообщениями (MessageExchange) между объектами в определенном потоке.
1) Looper. Поток может сгенерировать объект Looper, который управляет MessageQueue (очередью сообщений) в этом потоке.
2) Обработчик: вы можете создать объект-обработчик для связи с Looper для отправки новых сообщений в MessageQueue или для получения сообщений, отправленных Looper из очереди сообщений).
3) Очередь сообщений (очередь сообщений): используется для хранения сообщений, помещаемых потоком.
4) Тема:UIthread обычно является основным потокомИAndroidMessageQueue будет создан для него при запуске программы.
1.Handler создать сообщение
Каждое сообщение должно быть обработано указанным обработчиком, и эту функцию можно выполнить, создав сообщение через обработчик. Пул сообщений был введен в механизм сообщений Android. Когда обработчик создает сообщение, он сначала запрашивает, есть ли сообщение в пуле сообщений, а если сообщение есть прямо из пула сообщений, если нет, он повторно инициализирует экземпляр сообщения. Преимущество использования пула сообщений заключается в том, что когда сообщение не используется, оно не собирается как мусор, а помещается в пул сообщений, который можно использовать в следующий раз, когда обработчик создает сообщение. Пул сообщений улучшает повторное использование объектов сообщений и уменьшает количество системных сборок мусора.Процесс создания сообщенияКак показано
2.Хандлер отправляет сообщение
Процесс инициализации Handler, Looper, MessageQueueКак показано:
Хандер содержит ссылки на MessageQueue и Message Looper основного потока пользовательского интерфейса, а дочерние потоки могут отправлять сообщения в MessageQueue потока пользовательского интерфейса через обработчик.
3. обработчик сообщений
Основной поток пользовательского интерфейса запрашивает очередь сообщений UI_MQ через цикл Looper. Когда сообщение найдено, сообщение удаляется из очереди сообщений. Сначала проанализируйте сообщение, определите обработчик, соответствующий сообщению, с помощью параметров сообщения, а затем передайте сообщение указанному обработчику для обработки.
Процесс связи между дочерним потоком и основным потоком пользовательского интерфейса через Handler и LooperКак показаноКак показано
Многие люди должны были спросить в интервью, могу я спроситьAndroidКакая связь между Looper, Handler, Message в? Цель этого блога состоит в том, чтобы сначала представить взаимосвязь между этими тремя аспектами с точки зрения исходного кода, а затем дать заключение, которое легко запомнить.
1. Обзор
Handler, Looper и Message связаны с концепцией потока асинхронной обработки сообщений Android. Так что же такое поток асинхронной обработки сообщений?
После запуска потока асинхронной обработки сообщений он будет вводить тело бесконечного цикла. Каждый раз он будет принимать сообщение из своей внутренней очереди сообщений, затем вызывать соответствующую функцию обработки сообщений и выполнять После завершения сообщения продолжайте цикл. Если очередь сообщений пуста, поток заблокирует ожидание.
2. Анализ исходного кода
1、Looper
Для Looper существуют в основном методы prepare () и loop ().
Сначала посмотрите на метод prepare ()
sThreadLocal является объектом ThreadLocalВы можете хранить переменные в потоке. Как видите,В строке 5 поместите экземпляр Looper в ThreadLocalиСтроки 2-4 определяют, является ли sThreadLocal нулевым, в противном случае выдается исключение, Это означает, что метод Looper.prepare () не может быть вызван дважды, а такжеУбедитесь, что в потоке есть только один экземпляр Looper
Я считаю, что некоторые друзья должны столкнуться с этой ошибкой.
Давайте посмотрим на метод построения Looper:
В методе построения создается MessageQueue (очередь сообщений).
Затем мы рассмотрим метод loop ():
Строка 14: получите экземпляр Looper, сохраненный текущим потоком через Looper.myLooper (), а затем получите MessageQueue (очередь сообщений), сохраненную в этом экземпляре Looper, в строке 19.Убедитесь, что экземпляр обработчика связан с MessageQueue в нашем экземпляре Looper.
Тогда посмотрите на наш наиболее часто используемый метод sendMessage
Параметр messageQueueObj, переданный здесь, является объектом очереди сообщений, который мы создали ранее на уровне Java, а gMessageQueueClassInfo.mPtr означает смещение переменной-члена mPtr в классе Java MessageQueue. Через это смещение вы можете поместить это Локальный объект очереди сообщений natvieMessageQueue сохраняется в переменной-члене mPtr объекта очереди сообщений, созданного уровнем Java.Это для последующих вызовов других функций-членов объекта очереди сообщений уровня Java для входа в уровень JNI, который можно легко получить на уровне JNI. Объект очереди сообщений, соответствующий слою.
Давайте вернемся к конструктору NativeMessageQueue и посмотрим, как создается объект Looper слоя JNI, то есть, как реализован его конструктор. Этот класс Looper реализован в файле frameworks / base / libs / utils / Looper.cpp в:
То, что делает этот конструктор, очень важно: оно связано со знанием того, что основной поток приложения, который мы представим позже, перейдет в состояние ожидания, когда в очереди сообщений нет сообщений, и разбудит основной поток приложения, когда в очереди сообщений будет сообщение. Точки тесно связаны. В основном он создает конвейер через системный вызов pipe:
Чтобы использовать механизм epoll системы Linux, вы должны сначала создать специфичный для epoll файловый дескриптор через epoll_create:
Затем необходимо использовать функцию epoll_ctl, чтобы сообщить epoll, какое событие отслеживать соответствующий дескриптор файла:
Здесь нужно сообщить mEpollFd, что он хочет отслеживать событие EPOLLIN файлового дескриптора mWakeReadPipeFd, то есть, когда содержимое в конвейере доступно для чтения, он активирует поток, ожидающий в данный момент содержимое в конвейере.
После создания объекта Looper уровня C ++ он возвращается к конструктору NativeMessageQueue уровня JNI и, наконец, возвращается к процессу создания MessageQueue MessageQueue уровня Java. Таким образом, уровень Java Объект Looper готов. Это немного сложно. Давайте подведем итоги того, что мы сделали на этом шаге:
О. На уровне Java создается объект Looper. Этот объект Looper используется для входа в цикл сообщений. В нем есть очередь сообщений MessageQueue, объект mQueue.
B. На уровне JNI создается объект NativeMessageQueue.Этот объект NativeMessageQueue хранится в переменной-члене mPtr объекта очереди сообщений mQueue на уровне Java;
C. На уровне C ++ объект Looper создается и сохраняется в переменной-члене mLooper объекта NativeMessageQueue на уровне JNI. Роль этого объекта заключается в том, чтобы заставить основной поток приложения Android входить, когда в очереди сообщений уровня Java нет сообщения. Состояние ожидания, и когда в очередь сообщений уровня Java поступает новое сообщение, основной поток приложения Android просыпается для обработки сообщения.
Вернитесь к основной функции класса ActivityThread.После того, как вышеуказанная работа будет готова, вызовите функцию Loop класса Looper, чтобы войти в цикл сообщений:
Наиболее важным удобством этой функции является получение следующего сообщения для обработки из очереди сообщений, а именно функция MessageQueue.next, которая реализует файл frameworks / base / core / java / android / os / MessageQueue.java:
Если в очереди сообщений есть сообщение, а текущее время больше или равно времени выполнения в сообщении, то непосредственно верните это сообщение обработке сообщений Looper.loop, в противном случае вам придется дождаться времени выполнения сообщения:
Если в очереди сообщений нет сообщений, они будут переходить в бесконечное состояние ожидания, пока не появится новое сообщение:
-1 означает, что при следующем вызове nativePollOnce, если в сообщении нет сообщения, он перейдет в состояние бесконечного ожидания.
Рассчитанное здесь время ожидания используется при следующем вызове nativePollOnce.
Он имеет только одну функцию-член queueIdle. Когда эта функция выполняется, если возвращаемое значение равно false, IdleHandler будет удален из приложения, в противном случае IdleHandler будет продолжать поддерживаться в приложении. Тогда держи этот IdleHandler. MessageQueue предоставляет addIdleHandler и removeIdleHandler для регистрации и удаления IdleHandler.
Вернемся к функции MessageQueue, а затем посмотрим, существует ли IdleHandler, который необходимо выполнить перед переходом в состояние ожидания:
Эта функция сначала получает объект NatvieMessageQueue, созданный на уровне JNI, когда объект MessageQueue был создан на уровне Java, путем передачи входящего параметра ptr, а затем вызывает его функцию pollOnce:
Здесь операция передается в функцию pollOnce объекта mLooper. Здесь объект mLooper является объектом на уровне C ++. Он также создается при создании объекта NatvieMessageQueue на уровне JNI. Его функция pollOnce определена в frameworks / base / libs / В файле utils / Looper.cpp:
Чтобы облегчить обсуждение, мы удалили все нерелевантные части этой функции.В основном она вызывает функцию pollInner для дальнейших операций. Если возвращаемое значение pollInner не равно 0, эта функция может возвращаться.
Определение функции pollInner выглядит следующим образом:
Здесь соответствующие параметры инкапсулированы в объект ActivityClientRecord r, а затем вызывается функция queueOrSendMessage для добавления нового сообщения (H.LAUNCH_ACTIVITY) в очередь сообщений приложения. Эта функция определена в frameworks / base / core / java / android / app /ActivityThread.java file:
Этот класс H обрабатывает сообщения через функцию-член handleMessage, которую мы увидим позже, когда проанализируем обработку сообщений.
Когда была создана переменная-член mH класса ActivityThread? Когда мы проанализировали цикл обработки сообщений приложения, мы сказали, что при запуске процесса приложения он загрузит основную функцию класса ActivityThread.В этой основной функции перед входом в цикл обработки сообщений через класс Looper он будет находиться в текущем процессе. Создайте экземпляр ActivityThread:
При создании этого экземпляра его переменная-член mH будет создана одновременно:
Как упоминалось ранее, класс H наследуется от класса Handler, поэтому при создании объекта H вызывается конструктор класса Handler. Эта функция определена в файле frameworks / base / core / java / android / os / Handler.java:
Получив этот объект Looper, вы можете получить доступ к очереди сообщений приложения через Looper.mQueue.
Имея этот объект-обработчик mH, вы можете добавлять новые сообщения в очередь сообщений приложения через него. Возвращаясь к предыдущей функции queueOrSendMessage, когда она готовит объект сообщения msg, она начинает вызывать функцию mH.sendMessage для отправки сообщения.Эта функция определена в frameworks / base / core / java / android / os / Handler.java В файле:
Объект NativeMessageQueue уровня JNI был создан во время предыдущего анализа цикла сообщений и сохранен в переменной-члене mPtr объекта MessageQueue уровня Java.После его получения здесь вызывается его функция пробуждения для активации основного приложения. Поток, эта функция также определена в файле frameworks / base / core / jni / android_os_MessageQueue.cpp:
Здесь он выполняет операции через переменную-член mLooper для переменной wake. Здесь переменная-член mLooper представляет собой объект Looper, реализованный слоем C ++, который определен в файле frameworks / base / libs / utils / Looper.cpp:
В настоящее время, поскольку содержимое в конвейере доступно для чтения, основной поток приложения будет возвращаться из функции pollInner класса Looper здесь к функции nativePollOnce уровня JNI и, наконец, возвращаться к функции MessageQueue.next на уровне Java. Вы обнаружите, что в очереди сообщений есть новое сообщение, которое необходимо обработать, и Yu обработает сообщение.
3. Обработка информации
В предыдущем анализе цикла сообщений я говорил, что основным потоком приложения является процесс цикла сообщений в функции-члене Loop класса Looper. Эта функция определена в файле frameworks / base / core / java / android / os / Looper.java :
После получения объекта сообщения msg из очереди сообщений он вызывает функцию dispatchMessage своей целевой переменной-члена для обработки сообщения. В предыдущем анализе отправки сообщения я говорил, что целевая переменная-член объекта сообщения msg устанавливается при отправке сообщения. Обычно обработчик используется для отправки сообщения, а обработчик используется для обработки сообщения.
Мы продолжаем анализировать обработку сообщения на примерах, приведенных в предыдущем анализе отправки сообщения. Как упоминалось ранее, вАнализ исходного кода процесса запуска приложения AndroidНа шаге 30 этой статьи ActivityManagerService уведомляет приложение, вызывая функцию ScheduleLaunchActivity класса ApplicationThread, которая может загрузить стандартную активность приложения, и функция ScheduleLaunchActivity класса ApplicationThread, наконец, инкапсулирует запрос в сообщение, а затем передает класс ActivityThread Переменная-член mH для добавления этого сообщения в очередь сообщений приложения. Теперь нам нужно обработать это сообщение, поэтому мы вызовем функцию dispatchMessage класса H для обработки.
Класс H не реализует свою собственную функцию dispatchMessage, но он наследует функцию dispatchMessage родительского класса Handler.Эта функция определена в файле frameworks / base / core / java / android / os / Handler.java:
Переменная члена обратного вызова объекта сообщения msg и переменная члена mCallBack класса Handler обычно имеют значение null, поэтому для обработки сообщения будет вызываться функция handleMessage класса Handler, поскольку класс H переписывает функцию handleMessage при наследовании класса Handler Поэтому здесь на самом деле вызывается функция handleMessage класса H. Эта функция определена в файле frameworks / base / core / java / android / app / ActivityThread.java:
До сих пор мы анализировали механизм обработки сообщений приложения Android из трех частей цикла обработки сообщений: отправки и обработки сообщений. Для более глубокого понимания здесь мы суммируем некоторые из основных моментов:
О. Механизм обработки сообщений приложения Android состоит из трех частей: цикл обработки сообщений, отправка сообщений и обработка сообщений.
C. То, как основной поток приложения Android переходит в состояние ожидания ожидания, фактически ожидает чтения нового контента в конце чтения конвейера, в частности, через функцию epoll_wait в механизме Epoll системы Linux.
D. При добавлении нового сообщения в очередь сообщений приложения Android контент будет одновременно записываться в конец записи конвейера.Таким образом можно разбудить основной поток приложения, ожидающего сообщение.
E. Когда основной поток приложения, прежде чем войти в режим ожидания, будет ожидать, что текущий поток обрабатывает состояние ожидания, поэтому он вызовет зарегистрированный интерфейс IdleHandler, чтобы приложение могло обрабатывать некоторые вещи, когда оно находится в режиме ожидания.







































