Concurrent
https://developer.android.com/studio/write/annotations#thread-annotations |
https://developer.android.com/reference/android/os/Looper |
https://developer.android.com/guide/components/processes-and-threads |
07.12.2020 | https://youtu.be/iWiSQydw1qk |
Main Thread
Основной поток выполнения, который обрабатывает пользовательский интерфейс и выполняет все операции, связанные с отображением и взаимодействием с пользователем. Все события пользовательского ввода, такие как касания экрана, нажатия кнопок и другие взаимодействия, обрабатываются в Main Thread. Это гарантирует, что интерфейс остается отзывчивым и быстро реагирует на действия пользователя. Этот поток нельзя блокировать долгими операциями иначе выбросится ANR (Application Not Response).
@MainThread
Обозначает, что метод или класс должен вызываться только из основного потока.
@MainThread
fun updateUI() {
// Код для обновления пользовательского интерфейса
}
@UiThread
Обозначает, что метод или класс должен вызываться только из UI-потока (поток пользовательского интерфейса). Более старая аннотация. Если аннотированный элемент является классом, то все методы в классе должны вызываться в потоке пользовательского интерфейса. Обычно поток пользовательского интерфейса приложения также является основным потоком. Однако при особых обстоятельствах поток пользовательского интерфейса приложения может не быть его основным потоком.
@UiThread
fun updateUI() {
// Код для обновления пользовательского интерфейса
}
Worker Thread
Позволяет выполнять задачи, которые могут занять продолжительное время (например, операции ввода-вывода, сетевые запросы) без задержек в пользовательском интерфейсе.
@WorkerThread
Указывает, что метод или класс должен вызываться только из фоново́го потока.
@WorkerThread
fun performBackgroundOperation() {
// Код для выполнения фоново́й задачи
}
Message
Сообщение содержащее описание и произвольный объект данных которое можно отправить в Handler
. Message
можно создать через обычный конструктор однако так лучше не делать. Для создания лучше использовать метод Message.obtain
. Message.obtain
возвращает объект Message
из пула. Сделано это для оптимизации чтобы не тратить память каждый раз когда там нужен Message
. В этом пуле может быть максимум 50 сообщений. Если все сообщения пула используются то Message.obtain
создает и возвращает новый объект Message
.
Методы:
• callback
- runnable который выполнит Looper.
• next
- ссылка на следующий Message.
• time
- отметка времени когда сообщение должно быть выполнено
MessageQueue
Специальная очередь из объектов Message
простой односвязный список. Модель построенная на очереди и сообщениях позволяет откладывать выполнение задачи, отменять не нужные задачи и самое важное: эта модель позволяет делать GUI в одном потоке. Один поток позволяет не париться о том меняет ли эту View
какой-то другой поток или нет.
Handler
Отправляет сообщения Message
через MessageQueue
в Looper
. Handler
предоставляет удобный API для отправки Message
в Looper
. В примере ниже мы создаем Runnable с вызовом метод doSmth(), далее Handler
обернет этот Runnable в Message
и отправит в Looper
который его выполнит. Когда вызываем метод post, переданный Runnable оборачивается в Message
и отправляется на Looper
потока. Асинхронность UI обусловлена вызовом методов жизненного цикла через Looper
. Каждый раз когда мы создаем транзакцию для показа фрагмента, после вызова метода commit эта транзакция отправляется через Handler
в Looper
главного потока. Эта транзакция выполняется позже. Это же происходит и с показом новой Activity
анимациями и многими другими вещами.
val looper: Looper = requireNotNull(Looper.myLooper())
val handler: Handler = Handler(looper)
handler.post {
doSmth()
}
• какое-либо UI-событие посылается хэндлером в MessageQueue, оттуда достается о обрабатывается лупером.
Looper
Бесконечный цикл который получает из очереди MessageQueue
сообщения и выполняет их. Чтобы создать Looper
нужно вызвать метод Looper.prepare
. После этого метод Looper.prepare
сохраняет созданный объект Looper
в ThreadLocal
. Looper
у потока может быть только один.
• цикл бесконечный отрисовывать UI нужно постоянно.
ThreadLocal
Это просто ConcurrentHashMap<Thread, T>
коллекция в которой ключом является объект потока, а значением любой объект который нам нужен. Этот Map позволяет внутри потока достать объект предназначенный для него в любой момент времени. Из любого потока можно через Looper.myLooper
получить Looper
связанный с текущим потоком. При условии что Looper
есть в этом потоке.
runOnUiThread
Метод Activity
. Изменить View
из фонового потока. Под капотом отправляет Runnable через Handler
в Looper
главного потока.
Android Concurrent. Вопросы на собесе
- Что такое основной поток (UI Thread) в Android, и какие задачи следует выполнять в этом потоке?
- Что такое Handler, и как он взаимодействует с Looper?
- Как создать собственный поток с Looper и Handler?
- Как работает MessageQueue, и как она взаимодействует с Handler и Looper?
- Как передать данные между потоками через Handler?
- Как правильно обработать отложенные и повторяющиеся задачи с помощью Handler?
- Что такое Thread Pool, и как его использовать в Android?
- Какие есть способы в Android выполнить задачу в фоновом потоке?
•
Handler
иHandlerThread
•
Executor
иThreadPoolExecutor
•
Coroutines
•
WorkManager
•
JobScheduler
•
IntentService
- Как гарантированно вызвать метод класса на Main-потоке?
•
Handler.post
•
runOnUiThread
•
View.post
•
launch(Dispatchers.Main)