Что использовать вместо устаревшего Handler() в Kotlin/Android?
Почему Handler() устарел и что теперь использовать?
В Android 11 (API 30) конструктор Handler() без явного указания Looper был помечен как deprecated. Это сделано для повышения надёжности: раньше вы неявно привязывались к Looper текущего потока, который мог оказаться фоновым, что приводило к ошибкам. Теперь нужно всегда указывать Looper явно. Вот правильные подходы.
1. Классический Handler с явным Looper
Самый простой способ — передать Looper.getMainLooper() для работы с UI:
val handler = Handler(Looper.getMainLooper())
handler.postDelayed({
// Этот код выполнится на главном потоке через 2 секунды
textView.text = "Прошло 2 секунды"
}, 2000)
Если вам нужно выполнить задачу в фоновом потоке, используйте HandlerThread:
val handlerThread = HandlerThread("BackgroundThread")
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper)
backgroundHandler.post {
// Тяжёлая работа (не на главном потоке)
val result = doLongTask()
// Переключаемся на главный для обновления UI
Handler(Looper.getMainLooper()).post {
textView.text = result
}
}
2. Современный подход: Kotlin Coroutines (рекомендуется)
Для новых проектов Google рекомендует использовать корутины. Они лаконичнее и безопаснее. Подключите зависимость в build.gradle:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
Теперь задержка и переключение потоков выглядят так:
// В Activity или ViewModel
lifecycleScope.launch {
delay(2000) // Неблокирующая задержка
textView.text = "Прошло 2 секунды (корутина)"
}
// Для фоновой задачи
lifecycleScope.launch(Dispatchers.IO) {
val result = doLongTask() // Выполняется в фоне
withContext(Dispatchers.Main) {
textView.text = result // Возвращаемся на главный
}
}
3. View.postDelayed (для простых UI-задержек)
Если вам нужно просто отложить выполнение кода, связанного с конкретным View, можно использовать метод самого View:
textView.postDelayed({
textView.text = "Прошло 2 секунды"
}, 2000)
Этот метод не устарел и автоматически работает на главном потоке.
Рекомендации
- Для простых задержек в UI — используйте
View.postDelayed()илиHandler(Looper.getMainLooper()). - Для сложной асинхронности (сетевые запросы, БД, многопоточность) — переходите на корутины. Это современный стандарт Android-разработки.
- Не используйте пустой
Handler()— это источник багов.
Пример полного класса с корутинами:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
delay(1000)
findViewById<TextView>(R.id.textView).text = "Привет из корутины"
}
}
}
Корутины легко отменяются при уничтожении Activity (через lifecycleScope), что предотвращает утечки памяти.
Альтернативный ответ: Использование HandlerThread для фоновых задач
Если вы не хотите менять архитектуру на корутины, но нужно выполнять задачи в фоне с возможностью отмены, можно использовать HandlerThread в сочетании с Handler. Вот простой пример:
class MyBackgroundTask {
private val thread = HandlerThread("WorkerThread").apply { start() }
private val handler = Handler(thread.looper)
fun doDelayedWork(delayMs: Long, action: () -> Unit) {
handler.postDelayed(action, delayMs)
}
fun cancelAll() {
handler.removeCallbacksAndMessages(null)
}
fun cleanup() {
thread.quitSafely()
}
}
Использование:
val task = MyBackgroundTask()
task.doDelayedWork(2000) {
// Выполнится через 2 секунды в фоновом потоке
Log.d("TAG", "Фоновая работа")
// Для UI используйте Handler(Looper.getMainLooper())
}
// При уничтожении Activity
override fun onDestroy() {
task.cleanup()
super.onDestroy()
}
Этот подход хорош, когда нужно управлять очередью задач вручную, но он требует явного освобождения ресурсов. Для большинства современных проектов корутины удобнее.
Ответов пока нет.
Войдите чтобы оставить ответ