Что это такое и как их правильно использовать?

Многопоточность - одна из наиболее ценных концепций любого языка программирования. Умелое использование потоков на Android может помочь вам повысить производительность вашего приложения.

Когда пользователь открывает приложение, Android создает свой собственный процесс Linux. Помимо этого, система создает поток выполнения для этого приложения, называемый основным потоком или потоком пользовательского интерфейса.

Основной поток - это не что иное, как поток-обработчик. Основной поток отвечает за обработку событий со всего приложения, таких как обратные вызовы, связанные с информацией о жизненном цикле, или обратные вызовы из входных событий, или за обработку событий из других приложений и т. Д.

Любой блок кода, который необходимо запустить, помещается в рабочую очередь, а затем обслуживается основным потоком. Поскольку основной поток выполняет очень много работы, лучше предложить более длительную работу другим потокам, чтобы не мешать потоку пользовательского интерфейса выполнять его обязанности по рендерингу.

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

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

Они выполняются синхронно, что означает, что пользовательский интерфейс не будет отвечать, пока задача не будет завершена.

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

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

В этой статье давайте рассмотрим поток, обработчик, цикл и очередь сообщений.

Нить

Поток можно определить как путь, по которому выполняется программа. Виртуальная машина Java позволяет приложению иметь несколько потоков выполнения, работающих одновременно.

Параллелизм означает параллельное выполнение нескольких задач, это одна из основных причин, по которой мы используем потоки. Поскольку Android - это однопоточная модель, нам нужно создать разные потоки для выполнения нашей задачи и опубликовать результат в основном потоке, где обновляется пользовательский интерфейс.

Мы можем создавать потоки двумя способами.

  1. Расширяя класс Thread.

2. Реализуя Runnable интерфейс.

Интерфейс Runnable должен быть реализован любым классом, экземпляры которого предназначены для выполнения потоком. Класс должен определять метод без аргументов с именем run.

Каждый поток создается и управляется классом java.lang.Thread.

Нам нужно вызвать метод start в потоке, чтобы начать выполнение:

new Test.start()

У потока есть жизненный цикл с различными состояниями, такими как новый, готовый к работе, запущенный, неработающий (заблокированный), завершенный.

Мы можем выполнять любые операции внутри потоков, кроме обновления элементов пользовательского интерфейса. Чтобы обновить элемент пользовательского интерфейса из потока, нам нужно использовать либо обработчик, либо метод runOnUIThread.

runOnUiThread() выполняет указанное действие в потоке пользовательского интерфейса.

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

Сообщение, MessageQueue, Looper

MessageQueue - это очередь, в которой есть список задач (сообщений, исполняемых файлов), которые будут выполняться в определенном потоке. Android поддерживает MessageQueue в основном потоке.

Наряду с циклом и обработчиком MessageQueues являются частью строительных блоков потоковой передачи в Android и используются практически повсюду в системе.

Этот класс содержит список сообщений, которые должны быть отправлены петлителем. Вы можете просто позвонить Looper.myqueue(), чтобы получить список сообщений.

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

Мы можем получить MessageQueue для текущего потока с помощью Looper.myQueue().

Петлитель отвечает за поддержание активности нити. Это своего рода воркер, который обслуживает MessageQueue для текущего потока. Looper проходит через очередь сообщений и отправляет сообщения в соответствующие потоки для обработки.

Для каждой нити будет только один уникальный петлитель. Это означает, что только один MessageQueue на поток. Для одного потока может быть любое количество обработчиков. Итак, петлитель предоставляет потоку возможность работать в цикле с его собственным MessageQueue.

Сообщение определяет сообщение, содержащее описание и произвольный объект данных, который может быть отправлен обработчику.

Этот объект содержит два дополнительных поля int и дополнительное поле объекта, которые во многих случаях позволяют не выполнять выделение памяти. Мы можем просто сказать , что сообщение - это что-то вроде пакета, который используется для передачи данных.

Хотя конструктор сообщения является общедоступным, лучший способ получить один из них - это вызвать Message.obtain() или один из Handler.obtainMessage() методов, который извлечет их из пула переработанных объектов.

Есть разные аргументы, которые могут быть полезны:

public int what

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

public int arg1
public int arg2

arg1 и arg2 - недорогие альтернативы использованию setData(), если вам нужно сохранить только несколько целочисленных значений.

public Object obj

Произвольный объект для отправки получателю. При использовании Messenger для отправки сообщения между процессами это может быть только ненулевым, если он содержит Parcelable класса фреймворка (не реализованный приложением). Для других передач данных используйте setData.

Есть еще много аргументов, которые вы можете привести в документации.

Обработчик

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

Будет сложно управлять связью со всеми этими потоками, если вы управляете большой группой потоков. Итак, Android предоставил обработчики, чтобы упростить межпроцессное взаимодействие.

Обработчик позволяет отправлять и обрабатывать объекты Message и Runnable, связанные с MessageQueue потока. Каждый экземпляр обработчика связан с одним потоком и очередью сообщений этого потока.

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

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

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

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

Когда обработчик создан, он может получить объект Looper в конструкторе, который указывает, к какому потоку прикреплен обработчик. Если вы хотите использовать обработчик, прикрепленный к основному потоку, вам нужно использовать петлитель, связанный с основным потоком, вызывая Looper.getMainLooper().

Есть два основных использования обработчика:

  1. Чтобы запланировать выполнение сообщений и исполняемых файлов в какой-то момент в будущем.
  2. Чтобы поставить в очередь действие, которое будет выполняться в другом потоке, чем ваш собственный.

Как запланировать

Планирование сообщений выполняется с помощью методов post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long) и sendMessageDelayed(Message, long).

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

Версии sendMessage позволяют ставить в очередь объект Message, содержащий набор данных, который будет обрабатываться handleMessage(Message) методом обработчика (требующим реализации подкласса Handler).

Разница между post () и sendMessage ()

Мы используем post(), когда хотим выполнить некоторый код в потоке пользовательского интерфейса, ничего не зная о нашем Handler объекте. Это имеет смысл во многих случаях, когда в потоке пользовательского интерфейса необходимо выполнить произвольный код.

Например, чтобы обновить пользовательский интерфейс из фонового потока с некоторой задержкой, здесь вы можете создать обработчик, прикрепленный к потоку пользовательского интерфейса, а затем опубликовать действие как Runnable.

Но в некоторых случаях, если мы хотим организовать то, что отправляется в поток пользовательского интерфейса, и иметь определенные функции, которые мы хотим выполнить, в этом случае вы можете использовать sendMessage().

Резюме

Для выполнения длительных заданий необходимо создать поток. Handler - очень удобный объект для связи между двумя потоками.

Итак, у нас нет выбора между обработчиком и потоком. Используйте поток для выполнения тяжелых работ и используйте обработчик, если ваш фоновый поток инициирует выполнение задания в другом потоке - чаще всего потоке пользовательского интерфейса.

При использовании MessageQueue выполнение является последовательным, поэтому в случае параллельных потоков это позволит избежать состояния гонки. Обычно поток не может быть повторно использован после завершения его работы.

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

Пожалуйста, дайте мне знать ваши предложения и комментарии.

Спасибо за прочтение.