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
17.05.2022https://habr.com/ru/post/665806
26.11.2021https://habr.com/ru/companies/cian/articles/591877/
18.11.2021https://habr.com/ru/companies/cian/articles/589827/
10.11.2021https://habr.com/ru/company/cian/blog/588314
07.12.2020https://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. Вопросы на собесе
  1. Что такое основной поток (UI Thread) в Android, и какие задачи следует выполнять в этом потоке?
  1. Что такое Handler, и как он взаимодействует с Looper?
  1. Как создать собственный поток с Looper и Handler?
  1. Как работает MessageQueue, и как она взаимодействует с Handler и Looper?
  1. Как передать данные между потоками через Handler?
  1. Как правильно обработать отложенные и повторяющиеся задачи с помощью Handler?
  1. Что такое Thread Pool, и как его использовать в Android?
  1. Какие есть способы в Android выполнить задачу в фоновом потоке?

    Handler и HandlerThread

    Executor и ThreadPoolExecutor

    Coroutines

    WorkManager

    JobScheduler

    IntentService

  1. Как гарантированно вызвать метод класса на Main-потоке?

    Handler.post

    runOnUiThread

    View.post

    launch(Dispatchers.Main)