
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)