Android handler handlemessage dispatchmessage

Handler

java.lang.Object
android.os.Handler
Known Direct Subclasses
AsyncQueryHandler A helper class to help make handling asynchronous ContentResolver queries easier.
AsyncQueryHandler.WorkerHandler
HttpAuthHandler Represents a request for HTTP authentication.
SslErrorHandler Represents a request for handling an SSL error.

Class Overview

A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue . Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Scheduling messages is accomplished with the post(Runnable) , postAtTime(Runnable, long) , postDelayed(Runnable, long) , sendEmptyMessage(int) , sendMessage(Message) , sendMessageAtTime(Message, long) , and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler’s handleMessage(Message) method (requiring that you implement a subclass of Handler).

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler’s message queue and processed when appropriate.

Источник

Полный список

— создаем более содержательные сообщения для 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 для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Как реализованы Looper, Handler и MessageQueue?

В предыдущих постах мы описали что такое и для чего используются Looper, Handler, и MessageQueue. Иногда на собеседованиях просят написать свою имплементацию этих сущностей. Хоть эти классы и считаются низкоуровневым Android API, они по большей части реализованы обычными средствами Java.

По своей сути Looper, Handler и MessageQueue реализуют шаблон producer/consumer. Тред-продюсер отправляет сообщения через Handler в коллекцию-буфер, реализованную классом MessageQueue. Тред-потребитель блокирован с помощью класса Looper, который ожидает и принимает сообщения из MessageQueue и передает их на обработку хэндлеру.

Первый этап использования этих сущностей – инициализация лупера, которая выполняется методом Looper.prepare() . Этот метод создает объект-looper вызовом приватного конструктора. При вызове конструктора также создается объект MessageQueue, который хранится в приватном поле класса Looper.

После этого метод prepare() сохраняет созданный объект в статическое поле типа ThreadLocal , имеющее package видимость.

Реализация инициализации лупера довольна простая, но она позволяет в любом месте программы и из любого треда получить лупер и очередь сообщений, связанные с текущим тредом.

Статический метод Looper.myLooper() просто достает лупер из переменной ThreadLocal:

Метод Looper.myQueue() получает лупер методом myLooper() и возвращает поле queue:

В следующем посте разберемся, как реализовано добавление сообщений в очередь.

Тред-продюсер добавляет сообщение в очередь одним из методов post*() или sendMessage*() класса Handler .

Для начала вспомним, что Handler всегда связан с объектом Looper , а значит хэндлер имеет доступ к очереди сообщений ( MessageQueue ) лупера.

Методы post() , postAtTime() , postDelayed() добавляют в очередь сообщений объект Runnable , который будет выполнен тредом-потребителем.
Для этого сначала создается объект Message вызовом приватного метода getPostMessage(Runnable r) . getPostMessage() получает message из пула сообщений методом Message.obtain() и устанавливает runnable в поле callback.

Message.obtain() возвращает объект message из пула, который представляет собой связный список максимальным размером 50 сообщений. Если все сообщения пула используются, то obtain() создает и возвращает новый объект message.

После создания объекта message методы post*() вызывают один из методов sendMessage*() , передавая параметрами созданное сообщение и свои аргументы time или delay .

Вызов метода sendMessage(Message m) делегируется в sendMessageDelayed(m, 0) .

sendMessageDelayed(Message m, long delayMillis) прибавляет значение параметра delayMillis к текущему времени и делегирует вызов в метод sendMessageAtTime(Message m, long uptimeMillis) .

sendMessageAtTime() вызывает приватный метод enqueueMessage() , который устанавливает текущий хэндлер в поле target класса Message и вызывает enqueueMessage() у класса MessageQueue . Этот метод имеет package видимость и не доступен в публичном api.

MessageQueue – это связный список, реализованный с помощью поля next класса Message , которое ссылается на следующее сообщение в списке. Поле next также имеет package видимость.
Сообщения в MessageQueue отсортированы по возрастанию значения поля Message.when. Метод enqueueMessage() проходит по очереди, проверяя значение when каждого из сообщений и вставляет новое сообщение в положенное место очереди.
Код вставки сообщения в очередь в методе enqueueMessage() заключен в synchronized блок, который синхронизирован на this .

В предыдущих постах мы описали инициализацию лупера на потоке-потребителе и реализацию добавления сообщения в очередь потоком-продюсером.
Разберемся, как поток-потребитель получает сообщения из очереди.

Для блокировки потока и ожидания сообщения используется метод loop() .
Метод loop() вызывает метод MessageQueue.next() , который блокирует текущий поток и ожидает появления следующего сообщения.

Метод next() реализует бесконечный цикл, на каждой итерации которого сравнивает текущее время со значением поля when объекта message в голове очереди.
Если SystemClock.uptimeMillis() ≥ msg.when , то next() возвращает сообщение.
Если SystemClock.uptimeMillis() , то поток засыпает на время равное when — uptimeMillis .

Допустим поток вычислил when — uptimeMillis и заснул на минуту. Что будет, если хэндлер добавит в очередь новое сообщение со значением when — uptimeMillis равное 5 секунд, пока поток спит?
При вызове MessageQueue.enqueueMessage() сообщение добавляется в очередь и поток-потребитель пробуждается. Метод next() отрабатывает итерацию, в которой устанавливает новое значение времени пробуждения, равное 5 секундам.

Метод loop() , получив сообщение из next() , передает это сообщение на обработку хэндлеру, вызывая метод dispatchMessage(). Лупер получает хэндлер-обработчик из поля target :

Источник

Русские Блоги

Механизм обработки сообщений Android — Looper, Handler, Message (схема, исходный код)

Прежде чем приступить к обсуждению механизма обработки сообщений 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приходИдентификационная информацияДля обработки сообщений по-разному.

(4) Используйте setData () для храненияBundleОбъект. ? ? ?

2. Канал сообщений: Looper

При использовании обработчика для обработки сообщения вам необходимоLooper(Канал) для завершения. В одномВ Activity система автоматически запустит объект Looper для пользователя.Пока у пользователяВ пользовательском классе вам нужно вручную вызвать методы в классе Looper, прежде чем вы сможете нормально запустить объект Looper, Looper буквально означает «петух», и он был разработанИспользуется для создания нормальной нити в нити Looper, Так называемый поток Looper — это поток, который работает циклически. При разработке программ (особенно при разработке с графическим интерфейсом) нам часто требуется непрерывный цикл для выполнения цикла. После выполнения новой задачи он продолжает ожидать следующую задачу после выполнения. Это поток Looper. Использовать класс Looper для создания потока Looper очень просто:

public class LooperThread extends Thread <

public void run() <

// Инициализируем текущий поток в поток 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() <

if (sThreadLocal.get() != null ) <

// Попытка создать 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();

if (ident != newIdent) <

Log.wtf( «Looper» , «Thread identity changed from 0x»

+ 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 () завершает цикл петлителя:

public void 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.

if (mLooper == null ) <

throw new RuntimeException(

«Can’t create handler inside thread that has not called Looper.prepare()» );

// Важно! ! ! MQ, связанный с петлителем, непосредственно используется как его собственный MQ, поэтому его сообщение будет отправлено в MQ, связанный с петлителем

Ниже мы можем добавить обработчик для предыдущего класса LooperThread:

public class LooperThread extends Thread <

private Handler handler1;

private Handler handler2;

public void run() <

// Инициализируем текущий поток в поток 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)

Эти методы отправляют сообщения в MQ.Глядя на эти API, вы можете подумать, что обработчик может отправлять два вида сообщений, один из которых является объектом Runnable, а другой — объектом сообщения. Это интуитивное понимание, но на самом деле объект Runnable, созданный сообщением, наконец-то инкапсулируется в объект сообщения.Смотрите исходный код:

// Этот метод используется для отправки объекта 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» );

Log.w( «Looper» , e.getMessage(), e);

Сообщение, отправленное обработчиком, имеет следующие характеристики:

1.message.target — объект-обработчик, Который гарантирует, что петлитель может найти обработчик, который обрабатывает его, когда он достигает сообщения, то есть код ключа в методе loop ()

2. Сообщение, отправленное по почте,Его обратный вызов является объектом Runnable

Обработчик обработки сообщений

После отправки сообщения давайте посмотрим, как обработчик обрабатывает сообщение. Обработка сообщения осуществляется с помощью основного метода dispatchMessage (сообщение msg) и метода ловушки handleMessage (сообщение msg)

Завершено, см. Исходный код

  1. public void dispatchMessage(Message msg) <
  2. if (msg.callback != null) <
  3. handleCallback(msg);
  4. > else <
  5. if (mCallback != null) <
  6. if (mCallback.handleMessage(msg)) <
  7. return;
  8. >
  9. >
  10. handleMessage(msg);
  11. >
  12. >

Можно видеть, что, за исключением того, что методы run объектов handleMessage (Message) и Runnable реализованы разработчиками (реализуя специальную логику), внутренний рабочий механизм обработчика прозрачен для разработчиков. Обработчик имеет следующие две важные функции:

1) Обработчик может бытьОтправлять сообщения из любой темыЭти сообщения будут добавлены в связанный MQ

2)Обработка сообщения осуществляется с помощью основного метода dispatchMessage (Message msg) и метода ловушки handleMessage (Message msg).Хэндлер в немОбрабатывать сообщения в связанном потокеA.

Это решает проблему, заключающуюся в том, что Android не может обновить пользовательский интерфейс в других неосновных потоках.главная тема андроида — также тема петлителя(looper широко используется в Android), обработчик, который мы в нем создадим, будет по умолчанию связан с основным потоком MQ. Поэтому используйтеРешением обработчика является создание обработчика в действии и передача его ссылки в рабочий поток. После того, как рабочий поток выполнит задачу, используйте обработчик для отправки сообщения, чтобы уведомить действие об обновлении пользовательского интерфейса., (Процесс как показано)

Пример кода приведен ниже только для справки:

Android Handler механизм исследования заметки исследования(Принципиальная схема)

Предисловие: давным-давно, когда я изучал android, я вступил в контакт с Handler. Я знал, что Handler — это класс, используемый для связи между потоками. Он чаще всего используется для загрузки. Недавно я прочитал книгу Pro Android 3, и описанный в ней обработчик сказал: Очень подробно, с этим, запишите учебные заметки 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()<

Log.d( «ThreadUtils» , getThreadSignature());

public static void logThreadSignature(String name )<

Log.d( «ThreadUtils» , name + «:» +getThreadSignature());

public static void sleepForInSecs(int secs)<

Thread. sleep (secs * 1000);

>catch (Exception e) <

// TODO: handle exception

* Разговор о String в Bundle

public static Bundle getStringAsBundle(String message)<

Bundle b = new Bundle();

b.putString( «message» , message);

* Получить 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();

// Установите начальное значение нашего поля what, обратите внимание на порядок! ! !

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.Хандлер отправляет сообщение

Когда основной поток пользовательского интерфейса инициализирует первый обработчик, он создаст Looper через ThreadLocal.Индивидуальное соответствие между основным потоком Looper и UI, Цель использования ThreadLocal — убедиться, что каждый поток создает только один Looper. Позже, когда инициализируются другие обработчики, непосредственно получается Looper, созданный первым обработчиком. Когда Looper инициализируется, он создаст очередь сообщений MessageQueue. Пока чтоСоотношение между основным потоком, циклом сообщений и очередью сообщений составляет 1: 1: 1.

Процесс инициализации Handler, Looper, MessageQueueКак показано:

Хандер содержит ссылки на MessageQueue и Message Looper основного потока пользовательского интерфейса, а дочерние потоки могут отправлять сообщения в MessageQueue потока пользовательского интерфейса через обработчик.

3. обработчик сообщений

Основной поток пользовательского интерфейса запрашивает очередь сообщений UI_MQ через цикл Looper. Когда сообщение найдено, сообщение удаляется из очереди сообщений. Сначала проанализируйте сообщение, определите обработчик, соответствующий сообщению, с помощью параметров сообщения, а затем передайте сообщение указанному обработчику для обработки.

Процесс связи между дочерним потоком и основным потоком пользовательского интерфейса через Handler и LooperКак показаноКак показано

Многие люди должны были спросить в интервью, могу я спроситьAndroidКакая связь между Looper, Handler, Message в? Цель этого блога состоит в том, чтобы сначала представить взаимосвязь между этими тремя аспектами с точки зрения исходного кода, а затем дать заключение, которое легко запомнить.

1. Обзор

Handler, Looper и Message связаны с концепцией потока асинхронной обработки сообщений Android. Так что же такое поток асинхронной обработки сообщений?
После запуска потока асинхронной обработки сообщений он будет вводить тело бесконечного цикла. Каждый раз он будет принимать сообщение из своей внутренней очереди сообщений, затем вызывать соответствующую функцию обработки сообщений и выполнять После завершения сообщения продолжайте цикл. Если очередь сообщений пуста, поток заблокирует ожидание.

Сказав это, какое это имеет отношение к Handler, Looper и Message? Фактически, Looper отвечает за создание MessageQueue, а затем вводит тело бесконечного цикла для непрерывного чтения сообщений из MessageQueue, а создатель сообщения — один или несколько обработчиков.

2. Анализ исходного кода

1、Looper

Для Looper существуют в основном методы prepare () и loop ().
Сначала посмотрите на метод prepare ()

  1. public static final void prepare() <
  2. if (sThreadLocal.get() != null) <
  3. throw new RuntimeException(«Only one Looper may be created per thread»);
  4. >
  5. sThreadLocal.set(new Looper(true));
  6. >

sThreadLocal является объектом ThreadLocalВы можете хранить переменные в потоке. Как видите,В строке 5 поместите экземпляр Looper в ThreadLocalиСтроки 2-4 определяют, является ли sThreadLocal нулевым, в противном случае выдается исключение, Это означает, что метод Looper.prepare () не может быть вызван дважды, а такжеУбедитесь, что в потоке есть только один экземпляр Looper

Я считаю, что некоторые друзья должны столкнуться с этой ошибкой.
Давайте посмотрим на метод построения Looper:

  1. private Looper(boolean quitAllowed) <
  2. mQueue = new MessageQueue(quitAllowed);
  3. mRun = true;
  4. mThread = Thread.currentThread();
  5. >

В методе построения создается MessageQueue (очередь сообщений).
Затем мы рассмотрим метод loop ():

  1. public static void loop() <
  2. final Looper me = myLooper();
  3. if (me == null) <
  4. throw new RuntimeException(«No Looper; Looper.prepare() wasn’t called on this thread.»);
  5. >
  6. final MessageQueue queue = me.mQueue;
  7. // Make sure the identity of this thread is that of the local process,
  8. // and keep track of what that identity token actually is.
  9. Binder.clearCallingIdentity();
  10. final long ident = Binder.clearCallingIdentity();
  11. for (;;) <
  12. Message msg = queue.next(); // might block
  13. if (msg == null) <
  14. // No message indicates that the message queue is quitting.
  15. return;
  16. >
  17. // This must be in a local variable, in case a UI event sets the logger
  18. Printer logging = me.mLogging;
  19. if (logging != null) <
  20. logging.println(«>>>>> Dispatching to » + msg.target + » » +
  21. msg.callback + «: » + msg.what);
  22. >
  23. msg.target.dispatchMessage(msg);
  24. if (logging != null) <
  25. logging.println(» klass = getClass();
  26. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  27. (klass.getModifiers() & Modifier.STATIC) == 0) <
  28. Log.w(TAG, «The following Handler class should be static or leaks might occur: » +
  29. klass.getCanonicalName());
  30. >
  31. >
  32. mLooper = Looper.myLooper();
  33. if (mLooper == null) <
  34. throw new RuntimeException(
  35. «Can’t create handler inside thread that has not called Looper.prepare()»);
  36. >
  37. mQueue = mLooper.mQueue;
  38. mCallback = callback;
  39. mAsynchronous = async;
  40. >

Строка 14: получите экземпляр Looper, сохраненный текущим потоком через Looper.myLooper (), а затем получите MessageQueue (очередь сообщений), сохраненную в этом экземпляре Looper, в строке 19.Убедитесь, что экземпляр обработчика связан с MessageQueue в нашем экземпляре Looper.

Тогда посмотрите на наш наиболее часто используемый метод sendMessage

  1. public final boolean sendMessage(Message msg)
  2. <
  3. return sendMessageDelayed(msg, 0);
  4. >

  1. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) <
  2. Message msg = Message.obtain();
  3. msg.what = what;
  4. return sendMessageDelayed(msg, delayMillis);
  5. >

  1. public final boolean sendMessageDelayed(Message msg, long delayMillis)
  2. <
  3. if (delayMillis SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
  4. reinterpret_cast (nativeMessageQueue));
  5. >

Параметр messageQueueObj, переданный здесь, является объектом очереди сообщений, который мы создали ранее на уровне Java, а gMessageQueueClassInfo.mPtr означает смещение переменной-члена mPtr в классе Java MessageQueue. Через это смещение вы можете поместить это Локальный объект очереди сообщений natvieMessageQueue сохраняется в переменной-члене mPtr объекта очереди сообщений, созданного уровнем Java.Это для последующих вызовов других функций-членов объекта очереди сообщений уровня Java для входа в уровень JNI, который можно легко получить на уровне JNI. Объект очереди сообщений, соответствующий слою.

Давайте вернемся к конструктору NativeMessageQueue и посмотрим, как создается объект Looper слоя JNI, то есть, как реализован его конструктор. Этот класс Looper реализован в файле frameworks / base / libs / utils / Looper.cpp в:

  1. Looper::Looper(bool allowNonCallbacks) :
  2. mAllowNonCallbacks(allowNonCallbacks),
  3. mResponseIndex(0) <
  4. int wakeFds[2];
  5. int result = pipe(wakeFds);
  6. .
  7. mWakeReadPipeFd = wakeFds[0];
  8. mWakeWritePipeFd = wakeFds[1];
  9. .
  10. #ifdef LOOPER_USES_EPOLL
  11. // Allocate the epoll instance and register the wake pipe.
  12. mEpollFd = epoll_create(EPOLL_SIZE_HINT);
  13. .
  14. struct epoll_event eventItem;
  15. memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
  16. eventItem.events = EPOLLIN;
  17. eventItem.data.fd = mWakeReadPipeFd;
  18. result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
  19. .
  20. #else
  21. .
  22. #endif
  23. .
  24. >

То, что делает этот конструктор, очень важно: оно связано со знанием того, что основной поток приложения, который мы представим позже, перейдет в состояние ожидания, когда в очереди сообщений нет сообщений, и разбудит основной поток приложения, когда в очереди сообщений будет сообщение. Точки тесно связаны. В основном он создает конвейер через системный вызов pipe:

  1. int wakeFds[2];
  2. int result = pipe(wakeFds);
  3. .
  4. mWakeReadPipeFd = wakeFds[0];
  5. mWakeWritePipeFd = wakeFds[1];

Конвейер — это механизм межпроцессного взаимодействия в системе Linux. Подробнее см. В предыдущей статье.Android Learning StartupГлава 6 рекомендованной книги «Сценарный анализ исходного кода ядра Linux» — традиционное межпроцессное взаимодействие Uinx. Проще говоря, канал — это файл. На обоих концах канала есть два открытых файловых дескриптора. Эти два открытых файловых дескриптора соответствуют одному и тому же файлу, один из которых используется для чтения, другой — Для записи обычно используется то, что один поток читает содержимое конвейера, читая дескриптор файла. Когда у конвейера нет содержимого, этот поток переходит в состояние ожидания, в то время как другой поток записывает в дескриптор файла Запись содержимого в конвейер.При записи контента, если на другом конце есть поток, ожидающий контент в конвейере, этот поток будет пробужден. Как происходит эта операция ожидания и пробуждения? Это зависит от механизма epoll в системе Linux. Механизм epoll в системе Linux имеет улучшенные опросы для обработки больших пакетов дескрипторов и представляет собой расширенную версию мультиплексированного интерфейса ввода-вывода IO в Linux. Загрузка ЦП системы. Но здесь нам нужно только отслеживать интерфейс ввода-вывода только mWakeReadPipeFd, который является концом чтения конвейера, который мы создали ранее, зачем нам использовать epoll? Немного куриного ножа, чтобы убить курицу. На самом деле этот класс Looper очень мощный. Помимо мониторинга интерфейса конвейера, созданного внутри, он также предоставляет интерфейс addFd для вызовов внешнего интерфейса. Внешний мир может добавлять события ввода-вывода, которые он хочет отслеживать, через этот интерфейс. В этом объекте Looper, когда событие происходит на всех отслеживаемых интерфейсах ввода-вывода, соответствующий поток будет разбужен, чтобы справиться с ним, но здесь мы заботимся только о возникновении события ввода-вывода только что созданного конвейера.

Чтобы использовать механизм epoll системы Linux, вы должны сначала создать специфичный для epoll файловый дескриптор через epoll_create:

  1. mEpollFd = epoll_create(EPOLL_SIZE_HINT);

Входящий параметр EPOLL_SIZE_HINT — это максимальное количество дескрипторов файлов, которые можно отслеживать на этом mEpollFd.

Затем необходимо использовать функцию epoll_ctl, чтобы сообщить epoll, какое событие отслеживать соответствующий дескриптор файла:

  1. struct epoll_event eventItem;
  2. memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
  3. eventItem.events = EPOLLIN;
  4. eventItem.data.fd = mWakeReadPipeFd;
  5. result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);

Здесь нужно сообщить 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, чтобы войти в цикл сообщений:

  1. public class Looper <
  2. .
  3. public static final void loop() <
  4. Looper me = myLooper();
  5. MessageQueue queue = me.mQueue;
  6. .
  7. while (true) <
  8. Message msg = queue.next(); // might block
  9. .
  10. if (msg != null) <
  11. if (msg.target == null) <
  12. // No target is a magic identifier for the quit message.
  13. return;
  14. >
  15. .
  16. msg.target.dispatchMessage(msg);
  17. .
  18. msg.recycle();
  19. >
  20. >
  21. >
  22. .
  23. >

Здесь, чтобы войти в цикл сообщений, он постоянно получает следующее сообщение msg для обработки из очереди сообщений mQueue. Если целевая переменная-член сообщения имеет значение null, это означает выход из цикла сообщений, в противном случае он будет вызван Сообщение-функция dispatchMessage целевого объекта обрабатывает сообщение. Тип целевого объекта — Обработчик. Ниже, когда мы проанализируем отправку сообщения, мы увидим, что объект сообщения msg установлен.

Наиболее важным удобством этой функции является получение следующего сообщения для обработки из очереди сообщений, а именно функция MessageQueue.next, которая реализует файл frameworks / base / core / java / android / os / MessageQueue.java:

  1. public class MessageQueue <
  2. .
  3. final Message next() <
  4. int pendingIdleHandlerCount = -1; // -1 only during first iteration
  5. int nextPollTimeoutMillis = 0;
  6. for (;;) <
  7. if (nextPollTimeoutMillis != 0) <
  8. Binder.flushPendingCommands();
  9. >
  10. nativePollOnce(mPtr, nextPollTimeoutMillis);
  11. synchronized (this) <
  12. // Try to retrieve the next message. Return if found.
  13. final long now = SystemClock.uptimeMillis();
  14. final Message msg = mMessages;
  15. if (msg != null) <
  16. final long when = msg.when;
  17. if (now >= when) <
  18. mBlocked = false;
  19. mMessages = msg.next;
  20. msg.next = null;
  21. if (Config.LOGV) Log.v(«MessageQueue», «Returning message: » + msg);
  22. return msg;
  23. > else <
  24. nextPollTimeoutMillis = (int) Math.min(when — now, Integer.MAX_VALUE);
  25. >
  26. > else <
  27. nextPollTimeoutMillis = -1;
  28. >
  29. // If first time, then get the number of idlers to run.
  30. if (pendingIdleHandlerCount = when) <
  31. mBlocked = false;
  32. mMessages = msg.next;
  33. msg.next = null;
  34. if (Config.LOGV) Log.v(«MessageQueue», «Returning message: » + msg);
  35. return msg;
  36. > else <
  37. nextPollTimeoutMillis = (int) Math.min(when — now, Integer.MAX_VALUE);
  38. >
  39. > else <
  40. nextPollTimeoutMillis = -1;
  41. >

Если в очереди сообщений есть сообщение, а текущее время больше или равно времени выполнения в сообщении, то непосредственно верните это сообщение обработке сообщений Looper.loop, в противном случае вам придется дождаться времени выполнения сообщения:

  1. nextPollTimeoutMillis = (int) Math.min(when — now, Integer.MAX_VALUE);

Если в очереди сообщений нет сообщений, они будут переходить в бесконечное состояние ожидания, пока не появится новое сообщение:

  1. nextPollTimeoutMillis = -1;

-1 означает, что при следующем вызове nativePollOnce, если в сообщении нет сообщения, он перейдет в состояние бесконечного ожидания.

Рассчитанное здесь время ожидания используется при следующем вызове nativePollOnce.

Упомянутое здесь ожидание — ожидание в режиме ожидания, ожидание не занято, поэтому, прежде чем войти в состояние ожидания в режиме ожидания, если приложение регистрирует интерфейс IdleHandler для обработки некоторых вещей, сначала выполняется IdleHandler, а затем вводится состояние ожидания. IdlerHandler — это внутренний класс, определенный в MessageQueue:

  1. public class MessageQueue <
  2. .
  3. /**
  4. * Callback interface for discovering when a thread is going to block
  5. * waiting for more messages.
  6. */
  7. public static interface IdleHandler <
  8. /**
  9. * Called when the message queue has run out of messages and will now
  10. * wait for more. Return true to keep your idle handler active, false
  11. * to have it removed. This may be called if there are still messages
  12. * pending in the queue, but they are all scheduled to be dispatched
  13. * after the current time.
  14. */
  15. boolean queueIdle();
  16. >
  17. .
  18. >

Он имеет только одну функцию-член queueIdle. Когда эта функция выполняется, если возвращаемое значение равно false, IdleHandler будет удален из приложения, в противном случае IdleHandler будет продолжать поддерживаться в приложении. Тогда держи этот IdleHandler. MessageQueue предоставляет addIdleHandler и removeIdleHandler для регистрации и удаления IdleHandler.

Вернемся к функции MessageQueue, а затем посмотрим, существует ли IdleHandler, который необходимо выполнить перед переходом в состояние ожидания:

  1. // If first time, then get the number of idlers to run.
  2. if (pendingIdleHandlerCount (ptr);
  3. nativeMessageQueue->pollOnce(timeoutMillis);
  4. >

Эта функция сначала получает объект NatvieMessageQueue, созданный на уровне JNI, когда объект MessageQueue был создан на уровне Java, путем передачи входящего параметра ptr, а затем вызывает его функцию pollOnce:

  1. void NativeMessageQueue::pollOnce(int timeoutMillis) <
  2. mLooper->pollOnce(timeoutMillis);
  3. >

Здесь операция передается в функцию pollOnce объекта mLooper. Здесь объект mLooper является объектом на уровне C ++. Он также создается при создании объекта NatvieMessageQueue на уровне JNI. Его функция pollOnce определена в frameworks / base / libs / В файле utils / Looper.cpp:

  1. int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) <
  2. int result = 0;
  3. for (;;) <
  4. .
  5. if (result != 0) <
  6. .
  7. return result;
  8. >
  9. result = pollInner(timeoutMillis);
  10. >
  11. >

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

Определение функции pollInner выглядит следующим образом:

  1. int Looper::pollInner(int timeoutMillis) <
  2. .
  3. int result = ALOOPER_POLL_WAKE;
  4. .
  5. #ifdef LOOPER_USES_EPOLL
  6. struct epoll_event eventItems[EPOLL_MAX_EVENTS];
  7. int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
  8. bool acquiredLock = false;
  9. #else
  10. .
  11. #endif
  12. if (eventCount pendingResults,
  13. List pendingNewIntents, boolean notResumed, boolean isForward) <
  14. ActivityClientRecord r = new ActivityClientRecord();
  15. r.token = token;
  16. r.ident = ident;
  17. r.intent = intent;
  18. r.activityInfo = info;
  19. r.state = state;
  20. r.pendingResults = pendingResults;
  21. r.pendingIntents = pendingNewIntents;
  22. r.startsNotResumed = notResumed;
  23. r.isForward = isForward;
  24. queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
  25. >
  26. .
  27. >
  28. .
  29. >

Здесь соответствующие параметры инкапсулированы в объект ActivityClientRecord r, а затем вызывается функция queueOrSendMessage для добавления нового сообщения (H.LAUNCH_ACTIVITY) в очередь сообщений приложения. Эта функция определена в frameworks / base / core / java / android / app /ActivityThread.java file:

  1. public final class ActivityThread <
  2. .
  3. private final class ApplicationThread extends ApplicationThreadNative <
  4. .
  5. // if the thread hasn’t started yet, we don’t have the handler, so just
  6. // save the messages until we’re ready.
  7. private final void queueOrSendMessage(int what, Object obj) <
  8. queueOrSendMessage(what, obj, 0, 0);
  9. >
  10. .
  11. private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) <
  12. synchronized (this) <
  13. .
  14. Message msg = Message.obtain();
  15. msg.what = what;
  16. msg.obj = obj;
  17. msg.arg1 = arg1;
  18. msg.arg2 = arg2;
  19. mH.sendMessage(msg);
  20. >
  21. >
  22. .
  23. >
  24. .
  25. >

В функции queueOrSendMessage передаваемые выше параметры дополнительно инкапсулируются в объект сообщения msg, а затем объект сообщения msg добавляется в очередь сообщений приложения через функцию mH.sendMessage. Здесь mH — переменная-член класса ActivityThread, ее тип H, унаследованный от класса Handler, он определен в файле frameworks / base / core / java / android / app / ActivityThread.java:

  1. public final class ActivityThread <
  2. .
  3. private final class H extends Handler <
  4. .
  5. public void handleMessage(Message msg) <
  6. .
  7. switch (msg.what) <
  8. .
  9. >
  10. .
  11. >
  12. .
  13. >

Этот класс H обрабатывает сообщения через функцию-член handleMessage, которую мы увидим позже, когда проанализируем обработку сообщений.
Когда была создана переменная-член mH класса ActivityThread? Когда мы проанализировали цикл обработки сообщений приложения, мы сказали, что при запуске процесса приложения он загрузит основную функцию класса ActivityThread.В этой основной функции перед входом в цикл обработки сообщений через класс Looper он будет находиться в текущем процессе. Создайте экземпляр ActivityThread:

  1. public final class ActivityThread <
  2. .
  3. public static final void main(String[] args) <
  4. .
  5. ActivityThread thread = new ActivityThread();
  6. thread.attach(false);
  7. .
  8. >
  9. >

При создании этого экземпляра его переменная-член mH будет создана одновременно:

  1. public final class ActivityThread <
  2. .
  3. final H mH = new H();
  4. .
  5. >

Как упоминалось ранее, класс H наследуется от класса Handler, поэтому при создании объекта H вызывается конструктор класса Handler. Эта функция определена в файле frameworks / base / core / java / android / os / Handler.java:

  1. public class Handler <
  2. .
  3. public Handler() <
  4. .
  5. mLooper = Looper.myLooper();
  6. .
  7. mQueue = mLooper.mQueue;
  8. .
  9. >
  10. final MessageQueue mQueue;
  11. final Looper mLooper;
  12. .
  13. >

В конструкторе класса Hanlder это в основном начальные переменные-члены mLooper и mQueue. Здесь myLooper — это статическая функция-член класса Looper, с помощью которой получается объект Looper.Этот объект Looper создается функцией Looper.prepareMainLooper в основной функции класса ActivityThread, когда мы ранее анализировали цикл сообщений. Функция Looper.myLooper реализована в файле frameworks / base / core / java / android / os / Looper.java:

  1. public class Looper <
  2. .
  3. public static final Looper myLooper() <
  4. return (Looper)sThreadLocal.get();
  5. >
  6. .
  7. >

Получив этот объект Looper, вы можете получить доступ к очереди сообщений приложения через Looper.mQueue.

Имея этот объект-обработчик mH, вы можете добавлять новые сообщения в очередь сообщений приложения через него. Возвращаясь к предыдущей функции queueOrSendMessage, когда она готовит объект сообщения msg, она начинает вызывать функцию mH.sendMessage для отправки сообщения.Эта функция определена в frameworks / base / core / java / android / os / Handler.java В файле:

  1. public class Handler <
  2. .
  3. public final boolean sendMessage(Message msg)
  4. <
  5. return sendMessageDelayed(msg, 0);
  6. >
  7. public final boolean sendMessageDelayed(Message msg, long delayMillis)
  8. <
  9. if (delayMillis (ptr);
  10. return nativeMessageQueue->wake();
  11. >

Объект NativeMessageQueue уровня JNI был создан во время предыдущего анализа цикла сообщений и сохранен в переменной-члене mPtr объекта MessageQueue уровня Java.После его получения здесь вызывается его функция пробуждения для активации основного приложения. Поток, эта функция также определена в файле frameworks / base / core / jni / android_os_MessageQueue.cpp:

  1. void NativeMessageQueue::wake() <
  2. mLooper->wake();
  3. >

Здесь он выполняет операции через переменную-член mLooper для переменной wake. Здесь переменная-член mLooper представляет собой объект Looper, реализованный слоем C ++, который определен в файле frameworks / base / libs / utils / Looper.cpp:

  1. void Looper::wake() <
  2. .
  3. ssize_t nWrite;
  4. do <
  5. nWrite = write(mWakeWritePipeFd, «W», 1);
  6. > while (nWrite == -1 && errno == EINTR);
  7. .
  8. >

Функция Wake очень проста, просто записать строку «W» в конвейер, открыв дескриптор файла mWakeWritePipeFd. Фактически, не имеет значения, что записывается в конвейер, цель записи в конвейер состоит в том, чтобы разбудить основной поток приложения. Ранее, когда мы анализировали цикл сообщений приложения, мы говорили, что, когда в очереди сообщений приложения нет обработки сообщений, основной поток приложения перейдет в состояние ожидания ожидания, и это состояние ожидания ожидания вызывается этим классом Looper pollInner. В частности, функция, которую нужно ввести, — это вызвать функцию epoll_wait в функции pollInner, чтобы дождаться, когда содержимое в конвейере станет читаемым.

В настоящее время, поскольку содержимое в конвейере доступно для чтения, основной поток приложения будет возвращаться из функции pollInner класса Looper здесь к функции nativePollOnce уровня JNI и, наконец, возвращаться к функции MessageQueue.next на уровне Java. Вы обнаружите, что в очереди сообщений есть новое сообщение, которое необходимо обработать, и Yu обработает сообщение.

3. Обработка информации

В предыдущем анализе цикла сообщений я говорил, что основным потоком приложения является процесс цикла сообщений в функции-члене Loop класса Looper. Эта функция определена в файле frameworks / base / core / java / android / os / Looper.java :

  1. public class Looper <
  2. .
  3. public static final void loop() <
  4. Looper me = myLooper();
  5. MessageQueue queue = me.mQueue;
  6. .
  7. while (true) <
  8. Message msg = queue.next(); // might block
  9. .
  10. if (msg != null) <
  11. if (msg.target == null) <
  12. // No target is a magic identifier for the quit message.
  13. return;
  14. >
  15. .
  16. msg.target.dispatchMessage(msg);
  17. .
  18. msg.recycle();
  19. >
  20. >
  21. >
  22. .
  23. >

После получения объекта сообщения 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:

  1. public class Handler <
  2. .
  3. public void dispatchMessage(Message msg) <
  4. if (msg.callback != null) <
  5. handleCallback(msg);
  6. > else <
  7. if (mCallback != null) <
  8. if (mCallback.handleMessage(msg)) <
  9. return;
  10. >
  11. >
  12. handleMessage(msg);
  13. >
  14. >
  15. .
  16. >

Переменная члена обратного вызова объекта сообщения msg и переменная члена mCallBack класса Handler обычно имеют значение null, поэтому для обработки сообщения будет вызываться функция handleMessage класса Handler, поскольку класс H переписывает функцию handleMessage при наследовании класса Handler Поэтому здесь на самом деле вызывается функция handleMessage класса H. Эта функция определена в файле frameworks / base / core / java / android / app / ActivityThread.java:

  1. public final class ActivityThread <
  2. .
  3. private final class H extends Handler <
  4. .
  5. public void handleMessage(Message msg) <
  6. .
  7. switch (msg.what) <
  8. case LAUNCH_ACTIVITY: <
  9. ActivityClientRecord r = (ActivityClientRecord)msg.obj;
  10. r.packageInfo = getPackageInfoNoCheck(
  11. r.activityInfo.applicationInfo);
  12. handleLaunchActivity(r, null);
  13. > break;
  14. .
  15. >
  16. .
  17. >
  18. .
  19. >

Поскольку в примере, приведенном выше при анализе отправки сообщения, тип отправленного сообщения — H.LAUNCH_ACTIVITY, поэтому здесь будет вызываться функция handleLaunchActivity класса ActivityThread, чтобы действительно обработать сообщение, и на конкретный процесс можно будет ссылаться позже.Анализ исходного кода процесса запуска приложения AndroidЭта статья

До сих пор мы анализировали механизм обработки сообщений приложения Android из трех частей цикла обработки сообщений: отправки и обработки сообщений. Для более глубокого понимания здесь мы суммируем некоторые из основных моментов:

О. Механизм обработки сообщений приложения Android состоит из трех частей: цикл обработки сообщений, отправка сообщений и обработка сообщений.

Б. Прежде чем основной поток приложения Android войдет в процесс цикла сообщений, он создаст конвейер Linux (Pipe) для внутреннего использования. Цель этого конвейера — заставить основной поток приложения Android перейти в состояние ожидания ожидания, когда очередь сообщений пуста, и Это заставляет основной поток приложения просыпаться, когда в очереди сообщений приложения есть сообщение, которое необходимо обработать.

C. То, как основной поток приложения Android переходит в состояние ожидания ожидания, фактически ожидает чтения нового контента в конце чтения конвейера, в частности, через функцию epoll_wait в механизме Epoll системы Linux.

D. При добавлении нового сообщения в очередь сообщений приложения Android контент будет одновременно записываться в конец записи конвейера.Таким образом можно разбудить основной поток приложения, ожидающего сообщение.

E. Когда основной поток приложения, прежде чем войти в режим ожидания, будет ожидать, что текущий поток обрабатывает состояние ожидания, поэтому он вызовет зарегистрированный интерфейс IdleHandler, чтобы приложение могло обрабатывать некоторые вещи, когда оно находится в режиме ожидания.

Источник

Читайте также:  Что такое синтаксический анализ пакета андроид
Оцените статью