При работе приложения может сложиться необходимость отменить выполнение корутины. Например, в мобильном приложении запущена корутина для загрузки данных с некоторого интернет-ресуса, но пользователь решил перейти к другой странице приложения, и ему больше не нужны эти данные. В этом случае чтобы зря не тратить ресурсу системы, мы можем предусмотреть отмену выполнения корутины.
Для отмены выполнения корутины у объекта Job может применяться метод cancel():
import kotlinx.coroutines.* suspend fun main() = coroutineScope{ val downloader: Job = launch{ println("Начинаем загрузку файлов") for(i in 1..5){ println("Загружен файл $i") delay(500L) } } delay(800L) // установим задержку, чтобы несколько файлов загрузились println("Надоело ждать, пока все файлы загрузятся. Прерву-ка я загрузку...") downloader.cancel() // отменяем корутину downloader.join() // ожидаем завершения корутины println("Работа программы завершена") }
В данном случае определена корутина, которая имитирует загрузку файлов. В цикле пробегаемся от 1 до 5 и условно загружаем пять файлов.
Далее вызов метода downloader.cancel()
сигнализирует корутине, что надо прервать выполнение. Затем с помощью метода join()
ожидаем завершения
корутина, которая прервана. В итоге получим консольный вывод наподобие следующего:
Начинаем загрузку файлов Загружен файл 1 Загружен файл 2 Надоело ждать, пока все файлы загрузятся. Прерву-ка я загрузку... Работа программы завершена
Также вместо двух методов cancel()
и join()
можно использовать один сборный метод cancelAndJoin():
import kotlinx.coroutines.* suspend fun main() = coroutineScope{ val downloader: Job = launch{ println("Начинаем загрузку файлов") for(i in 1..5){ println("Загружен файл $i") delay(500L) } } delay(800L) println("Надоело ждать, пока все файлы загрузятся. Прерву-ка я загрузку...") downloader.cancelAndJoin() // отменяем корутину и ожидаем ее завершения println("Работа программы завершена") }
Все suspend-функции в пакете kotlinx.coroutines
являются прерываемыми (cancellable). Это значит, что они проверяют, прервана ли корутина.
И если ее выполнение прервано, они генерируют исключение типа CancellationException. И в самой корутине мы можем перехватить это исключение,
чтобы обработать отмену корутины. Например:
import kotlinx.coroutines.* suspend fun main() = coroutineScope{ val downloader: Job = launch{ try { println("Начинаем загрузку файлов") for(i in 1..5){ println("Загружен файл $i") delay(500L) } } catch (e: CancellationException ){ println("Загрузка файлов прервана") } finally{ println("Загрузка завершена") } } delay(800L) println("Надоело ждать. Прерву-ка я загрузку...") downloader.cancelAndJoin() // отменяем корутину и ожидаем ее завершения println("Работа программы завершена") }
Здесь код выполнения корутины обернут в конструкцию try. Если корутина будет прервана извне, то с помощью блока catch и
перехвата исключения CancellationException
мы сможем обработать отмену корутины.
И если нам надо выполнить некоторые завершающие действия, например, освободить используемые в корутине ресурсы - закрыть файлы, различные подключения к внешним ресурсам, то это можно сделать в блоке finally. Но в данном случае в этом блоке просто выводим диагностическое сообщение.
В итоге при вызове метода downloader.cancel()
производейт отмена корутины. Будет сгенерировано исключение, и в корутине в блоке catch
мы сможем ее
обработать. В итоге получим следующий консольный вывод:
Начинаем загрузку файлов Загружен файл 1 Загружен файл 2 Надоело ждать. Прерву-ка я загрузку... Загрузка файлов прервана Загрузка завершена Работа программы завершена
Подобным образом можно отменять выполнение и корутин, создаваемых с помощью функции async(). В этом случае обычно вызов метода await() помещается в блок try:
import kotlinx.coroutines.* suspend fun main() = coroutineScope{ // создаем и запускаем корутину val message = async { getMessage() } // отмена корутины message.cancelAndJoin() try { // ожидаем получение результата println("message: ${message.await()}") } catch (e:CancellationException){ println("Coroutine has been canceled") } println("Program has finished") } suspend fun getMessage() : String{ delay(500L) return "Hello" }
Консольный вывод программы:
Coroutine has been canceled Program has finished