Всем привет! Настало время для новой статьи, я решил сделать еще один перевод. Приятного чтения и погружения в Kotline Coroutines!
С самого начала определимся с парой фундаментальных определений, которые будут использоваться в этой статье.
Handler (подробнее)
A Handler allows you to send and process
Message
and Runnable objects associated with a thread'sMessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to aLooper
. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread. — Android Develoeprs
Другими словами, это инструмент благодаря которому можно отправлять какие-либо действия в другой поток. Следовательно тяжелые операции, такие как чтение файловой системы или получение/отправка данных по сети, могут выполняться в фоне и не загружать основной поток.
Looper (подробнее)
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call
prepare()
in the thread that is to run the loop, and thenloop()
to have it process messages until the loop is stopped. — Android Develoeprs
Если на русском, то Looper - это класс используемый для отправки сообщения в цикл потока. Потоки изначально не имеют цикла сообщений, ассоциированного с ними, в связи с чем, его нужно создать используя метод prepare()
и затем loop()
чтобы поток начал обрабатывать сообщения по очереди, до полной остановки.
В последних версиях Android использование Handler'ов без Looper'а стало устаревшим, а связано это с тем, что подобное поведение часто приводит к багам связанным с потерями сообщений, причем без уведомления, и крашам. Согласно документации, так же есть возможность использования Executor'a
, либо же получить обработчик конкретной view, как замену, но таким даже в таком случае приложение может грохнуться, так как не связанно с Lifecycle'ом
.
Подробнее про Lifecycle
можно почитать здесь.
Давайте посмотрим, как мы можем применить Kotlin Coroutines и Lifecycle библиотеки чтобы заменить Handler().postDelay() метод и безопасно вызвать какое-либо действие после определенного промежутка времени.
Handlers: старомодный способ
Не смотря на то, что конструктор Handler()
является устаревшим, у нас есть возможность получить обработчик View вызвав getHandler()
, либо же можно просто вызвать метод postDelay()
. Таким образом вы не будете использовать устаревший метод и будет возможность приостановить выполнение действия на определенное время.
Давайте посмотрим как это работает под капотом! Чтобы запустить код на обработчике, необходимо подучить доступ к ассоциированному к нему потоку, на котором работает View. Каждый раз, когда действие будет запущено используя postDelay, новое действие будет добавлено в очередь сообщений. Однако помните, что работа будет происходить на основном потоке!
Делая это, важно помнить, что ваше отсроченное действие может работать, даже когда View уже будет уничтожено, что может привести к неожиданному поведению и даже вылетам приложения.
Kotlin + Coroutine + Lifecycle
Давайте посмотрим, как сделать лучшее решение, чем использование Handler().postDelay()
, используя Kotlin, Coroutines и Lifecycle библиотеках. Вначале, необходимо будет подключить следующие библиотеки.
Теперь мы готовы. Чтобы достичь безопасного ожидания, необходимо найти lifecycleOwner необходимой View, используя findViewTreeLifecycleOwner()
метод предоставленный Lifecycle библиотекой.
Затем, используя Coroutine Scope полученного Lifecycle, мы можем запустить новую сопрограмму с выбранным диспетчер, мы будем использовать Main Dispatcher, так как хотим выполнить действия на основном потоке. Чтобы выполнить действия в сопрограмме с задержкой, мы применим функцию delay()
.
Теперь можем вызвать функцию delayOnLifecycle()
, которая теперь является extension-function для любой View.
Заключение
Всегда было болезненно управлять действиями на нескольких потоках, так как было необходимо смотреть состояние Lifecycle, Fragment и так далее. Однако благодаря Kotlin Coroutines и Lifecycle библиотеках, мы имеем более безопасное решение.
А вы уже используете Kotline Coroutines в своем проекте?
Большое спасибо, что прочитали мою новую статью, а так же огромное спасибо автору оригинала, Julien Salvi и Olivier Buiron. Я надеюсь на обратную связь!
Nihiroz
А что будет, если во время выполнения корутины мы удалим View с экрана и произойдет `onDetachedFromWindow`? Судя по всему корутина не отменится и произойдет утечка памяти. К тому же выполнение асинхронных операций внутри View говорит о плохой архитектуре, желательно выпость такие операции в ViewModel или другие подобные штуки.
Отдельное спасибо за код в виде картинок, очень удобно копировать
alyxe Автор
Да, действительно, если удалить View с экрана, а корутина была запущена во View, то необходимо проследить и отменить выполнение корутины.
Действительно если какая-то лишняя логика, особенно асинхронная, выполняется внутри View - это не очевидное и проблемное решение. Однако никто не мешает имплементировать анимацию используя корутины.
Да, я думал делать картинками или кодом, но решил сохранить в этом случае, как в оригинале. В следующий раз, постараюсь делать либо ссылку на GitHub, либо вставлять код вместо картинок.