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

Объект в Android, используемый для обмена данными между потоками через Handler и MessageQueue. Message позволяет отправить задачу или данные из одного потока в другой, обычно из фонового потока в основной (UI) поток, для обновления интерфейса.

fun main() {
    // Создаем Handler в основном потоке
    val handler = object: Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                1 -> Log.d("Handler", "Received message: ${msg.obj}")
                2 -> Log.d("Handler", "Received arg1: ${msg.arg1}, arg2: ${msg.arg2}")
            }
        }
    }

    // Создаем и отправляем сообщение
    val message = Message.obtain()
    message.what = 1
    message.obj = "Hello from background thread"
    handler.sendMessage(message)

    // Отправляем сообщение с аргументами
    val anotherMessage = handler.obtainMessage(2, 10, 20)
    handler.sendMessage(anotherMessage)
}

what целое число, используется для идентификации типа сообщения.

obj поле типа Object, можно использовать для передачи произвольных данных (например, строки, объекта).

target ссылка на Handler, который будет обрабатывать это сообщение.

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

callback хранит объект Runnable, который будет выполнен, когда сообщение будет обработано.

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

isAsynchronous флаг, позволяющий сообщению обрабатываться вне очереди в MessageQueue.

MessageQueue

Структура данных, управляющая очередью сообщений (Message) для обработки в главном или любом другом потоке с Looper. Она хранит задачи, которые нужно выполнить, и управляет их последовательностью.

Handler

Инструмент для общения между потоками. Он отправляет задачи или сообщения в очередь потока, связанную с Looper, и позволяет выполнять код в этом потоке, обычно для обновления UI.

val handler = Handler(Looper.getMainLooper())
handler.post {
    println("Running on: ${Thread.currentThread().name}")
}
handler.sendMessage(Message.obtain().apply { what = 1 })
Looper

Класс в Android, который управляет циклом обработки сообщений и событий (message loop). Используется для организации очереди событий в потоке, позволяя обрабатывать задачи последовательно. Чаще всего используется в основном (UI) потоке и в фоновом потоке с HandlerThread.

Handler(Looper.getMainLooper()).post {
    println("Running on UI thread")
}
Thread {
    Looper.prepare() // Инициализируем Looper
    val handler = Handler(Looper.myLooper()!!)
    handler.post {
        println("Task running in background thread")
    }
    Looper.loop() // Запускаем цикл обработки сообщений
}.start()
prepare

Инициализирует Looper в потоке (выполняется автоматически в UI-потоке).

loop

Запускает бесконечный цикл, обрабатывающий сообщения из MessageQueue.

HandlerThread

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

fun main() {
    // Создаем HandlerThread
    val handlerThread = HandlerThread("MyHandlerThread")
    handlerThread.start() // Запускаем поток

    // Создаем Handler, связанный с Looper из HandlerThread
    val handler = Handler(handlerThread.looper)

    // Отправляем задачу в фоновый поток
    handler.post {
        Log.d("HandlerThread", "Task running in background thread")
    }

    // Завершаем работу потока
    handlerThread.quitSafely()
}
quitSafely

Завершает работу потока безопасно, обработав оставшиеся задачи.

runOnUiThread

Позволяет обновить пользовательский интерфейс из фонового потока.

class MainActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Thread {
            // Фоновая операция
            Thread.sleep(2000)

            // Попытка обновить UI из фонового потока
            runOnUiThread {
                Toast.makeText(this, "Hello from UI thread", Toast.LENGTH_SHORT).show()
            }
        }.start()
    }
}
Вопросы на собесе (9)
  1. Что такое основной поток (UI Thread) в Android, и какие задачи следует выполнять в этом потоке?

    Основной поток (UI Thread) в Android отвечает за обновление интерфейса и обработку событий пользователя. В этом потоке выполняются задачи, связанные с UI, а долгие операции, такие как сетевые запросы, должны выполняться в фоновом потоке.

  1. Что такое Handler, и как он взаимодействует с Looper?

    Handler — класс для отправки и обработки сообщений в потоке, к которому он привязан через Looper. Он добавляет сообщения в очередь MessageQueue, и Looper поочередно обрабатывает их, вызывая методы Handler.

  1. Как создать собственный поток с Looper и Handler?

    Создайте класс, унаследованный от Thread.

    В методе run() инициализируйте Looper с помощью Looper.prepare(), создайте Handler и вызовите Looper.loop().

    Создайте экземпляр этого класса и запустите его.

  1. Как работает MessageQueue, и как она взаимодействует с Handler и Looper?

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

  1. Как передать данные между потоками через Handler?

    Передайте данные между потоками через Handler, отправив Message или Runnable в основной поток. Создайте Handler в основном потоке, затем используйте sendMessage() или post() для передачи данных, которые можно извлечь в методе handleMessage() или внутри Runnable.

  1. Как правильно обработать отложенные и повторяющиеся задачи с помощью Handler?

    Для отложенных задач используйте postDelayed(), а для повторяющихся — вызывайте postDelayed() в конце Runnable. Отменяйте задачи с помощью removeCallbacks() при необходимости.

  1. Можно ли выполнить сетевой запрос из главного потока в Android?

    Нет, выполнять сетевые запросы из главного потока в Android нельзя, так как это может привести к заморозке интерфейса и вызвать NetworkOnMainThreadException.

  1. Какие есть способы в Android выполнить задачу в фоновом потоке?

    Handler и HandlerThread

    Executor и ThreadPoolExecutor

    Coroutines

    WorkManager

    JobScheduler

    IntentService

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

    Handler.post

    runOnUiThread

    View.post

    launch(Dispatchers.Main)