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
Объект в 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)
- Что такое основной поток (UI Thread) в Android, и какие задачи следует выполнять в этом потоке?
Основной поток (UI Thread) в Android отвечает за обновление интерфейса и обработку событий пользователя. В этом потоке выполняются задачи, связанные с UI, а долгие операции, такие как сетевые запросы, должны выполняться в фоновом потоке.
- Что такое Handler, и как он взаимодействует с Looper?
Handler
— класс для отправки и обработки сообщений в потоке, к которому он привязан черезLooper
. Он добавляет сообщения в очередьMessageQueue
, иLooper
поочередно обрабатывает их, вызывая методыHandler
.
- Как создать собственный поток с Looper и Handler?
• Создайте класс, унаследованный от
Thread
.• В методе
run()
инициализируйтеLooper
с помощьюLooper.prepare()
, создайтеHandler
и вызовитеLooper.loop()
.• Создайте экземпляр этого класса и запустите его.
- Как работает MessageQueue, и как она взаимодействует с Handler и Looper?
MessageQueue
хранит сообщения и задачи, отправленные в основной поток.Looper
управляетMessageQueue
, извлекая и обрабатывая сообщения по очереди.Handler
используется для отправки сообщений вMessageQueue
, позволяя взаимодействовать сLooper
, чтобы выполнять код в основном потоке.
- Как передать данные между потоками через Handler?
Передайте данные между потоками через
Handler
, отправивMessage
илиRunnable
в основной поток. СоздайтеHandler
в основном потоке, затем используйтеsendMessage()
илиpost()
для передачи данных, которые можно извлечь в методеhandleMessage()
или внутриRunnable
.
- Как правильно обработать отложенные и повторяющиеся задачи с помощью Handler?
Для отложенных задач используйте
postDelayed()
, а для повторяющихся — вызывайтеpostDelayed()
в концеRunnable
. Отменяйте задачи с помощьюremoveCallbacks()
при необходимости.
- Можно ли выполнить сетевой запрос из главного потока в Android?
Нет, выполнять сетевые запросы из главного потока в Android нельзя, так как это может привести к заморозке интерфейса и вызвать
NetworkOnMainThreadException
.
- Какие есть способы в Android выполнить задачу в фоновом потоке?
•
Handler
иHandlerThread
•
Executor
иThreadPoolExecutor
•
Coroutines
•
WorkManager
•
JobScheduler
•
IntentService
- Как гарантированно вызвать метод класса на Main-потоке?
•
Handler.post
•
runOnUiThread
•
View.post
•
launch(Dispatchers.Main)