Пытаюсь лучше понять работу Kotlin Coroutine. Беру небольшую тему про Kotlin Coroutine и пытаюсь разобраться и написать шпаргалку (максимально кратко и лаконично). Сегодня сильно упрощенно о том, как выглядит скомпилированный класс корутины.
При создании и запуске корутины компилятор создаст специальный объект, который реализует интерфейс Continuation (другими словами, возможно будет понятнее: объект представляет собой реализацию лямбды, переданной в launch и у этого объекта будет своё соcтояние, которое будет меняться по мере выполнения кода, находящегося в этой лямбде). Этот класс содержит всю логику выполнения корутины, включая её состояние и локальные переменные.
Также будут созданы объекты для каждой suspend функции, но в этот раз их не рассматриваем.
Жизненный цикл корутин
Корутина проходит через несколько состояний в течение своего жизненного цикла:
1. Создание: Корутина создается с помощью строителей, таких как launch или async.
2. Запуск: Корутина начинает выполнение, когда её запускают с помощью start() или автоматически, если используется launch.
3. Приостановка: Корутина может быть приостановлена при вызове suspend-функции, например, delay() или await().
4. Возобновление: После завершения асинхронной операции корутина возобновляет выполнение с места приостановки.
5. Завершение: Корутина завершает выполнение, когда весь код внутри неё выполнен или когда она отменена.
Что такое Continuation
Continuation — это объект, который представляет собой состояние выполнения корутины и позволяет возобновить её выполнение после приостановки (это как бы callback, который вызывается, когда корутина готова продолжить выполнение после приостановки).
Continuation — это механизм, который позволяет Kotlin Coroutines приостанавливать и возобновлять выполнение. Он является ключевым элементом реализации suspend-функций и асинхронного программирования в Kotlin.
Когда корутина приостанавливается (например, при вызове suspend-функции), её состояние сохраняется в объекте Continuation. Этот объект содержит информацию о том, где именно корутина была приостановлена, и как её можно продолжить после завершения асинхронной операции.
В Kotlin Continuation — это интерфейс, который выглядит следующим образом:
interface Continuation<in T> { val context: CoroutineContext fun resumeWith(result: Result<T>) }
Пример.
Для следующего кода:
suspend fun mySuspendFunction() { println("Start") delay(1000) println("End") } launch { mySuspendFunction() }
Код будет состоять из двух частей:
-
println(«Start») и delay(1000) (весь код до первой suspend функции и сама функция);
-
println(«End») (весь оставшийся код, т.к. больше нет suspend функций).
Создастся следующий класс (код компилируется в байт-код, код на java привожу для упрощения):
class GeneratedContinuation extends SuspendLambda { int label; // Текущее состояние @Override Object invokeSuspend(Object result) { switch (label) { case 0: println("Start"); label = 1; if (delay(1000, this) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; // Приостановка // После возобновления переходит к case 1 case 1: println("End"); return Unit.INSTANCE; } throw IllegalStateException(); } }
Метка label — это указатель на текущую точку выполнения внутри корутины, он используется для отслеживания текущего состояния корутины. При каждом вызове suspend-функции значение label изменяется, чтобы указать, с какого места нужно продолжить выполнение после приостановки.
При первом вызове (label = 0) выполняется код до delay().
delay(1000, this) передаёт Continuation (this) в suspend функцию, для того, чтобы после окончания своей работы она вызвала invokeSuspend и работа продолжилась. При приостановке возвращается COROUTINE_SUSPENDED. COROUTINE_SUSPENDED — это специальное значение, которое возвращается, когда корутина приостанавливается. Это является сигналом для механизма корутин: «Выполнение приостановлено, нужно освободить текущий поток».
После истечения времени приостановки, т.е. после завершения suspend функции работа продолжается со следующим значением label. В нашем случае по окончанию delay(1000) выполнение продолжается с label = 1.
Возврат значения suspend функцией
Для возврата значений у нас есть в функции invokeSuspend параметр result.
Рассмотрим пример
launch { val data = fetchData() // suspend fun processData(data) // suspend fun println("Data is processed") }
Код будет состоять из трёх частей:
-
fetchData() (весь код до первой suspend функции и сама функция);
-
processData(data) (весь код после первой suspend функции и по вторую suspend функцию включительно);
-
println(«Data is processed») (весь оставшийся код, т.к. больше нет suspend функций).
class GeneratedContinuation extends SuspendLambda { int label; // Текущее состояние byte[] data; @Override Object invokeSuspend(Object result) { switch (label) { case 0: label = 1; data = fetchData(this); return COROUTINE_SUSPENDED; // Приостановка // После возобновления переходит к case 1 case 1: data = (byte[]) result; label = 2; processData(data, this); return COROUTINE_SUSPENDED; // Приостановка // После возобновления переходит к case 2 case 2: println("Data is processed"); return Unit.INSTANCE; } throw IllegalStateException(); } }
Т.е. suspend функция fetchData после окончания работы вызовет invokeSuspend на «объекте корутины» и передаст в качестве параметра result результат своей работы — массив байт data.
ссылка на оригинал статьи https://habr.com/ru/articles/882988/
Добавить комментарий