launch — същност, fire-and-forget стартиране на корутини и как работи

Автор: IT Sectr Публикувано: 2026-06-22 Време за четене: 7 мин

launch — Coroutine Builder в Kotlin, който стартира корутина без връщане на резултат, връщайки обект Job за управление на изпълнението. Този builder имплементира модела fire-and-forget: корутината започва работа във фонова нишка и автоматично завършва. Според документацията на JetBrains, 2024, launch е основният начин за стартиране на паралелни задачи в Kotlin Coroutines.

Основни точки

  • launch — builder за стартиране на корутини без връщане на резултат (fire-and-forget)
  • Job — обект за управление на корутина с методи cancel(), join(), isActive
  • launch се интегрира с CoroutineScope за автоматично отменяне на дъщерни корутини
  • CoroutineStart.LAZY позволява отлагане на стартирането до изрично извикване на start() или join()
  • launch в Android се използва за запазване в БД, мрежови повиквания и обновяване на UI

Какво е launch в Kotlin Coroutines?

launch — е разширяваща функция на Kotlin, достъпна чрез import kotlinx.coroutines, която създава нова корутина в зададен CoroutineScope. За разлика от обикновените функции, кодът вътре в launch се изпълнява конкурентно — корутината може да бъде спряна (suspend) без блокиране на нишката и да бъде възобновена по-късно.

Launch builder е фундаментален градивен блок на модела на корутините в Kotlin. Той не връща изчислена стойност, но връща обект Job, който позволява контрол на изпълнението на корутината. Всички дъщерни корутини, стартирани чрез launch вътре в друга корутина, са обвързани с родителската.

Синтаксис и параметри на launch

Launch builder е дефиниран като inline функция с няколко параметъра: CoroutineScope, CoroutineContext, CoroutineStart и suspend-блок. Параметрите имат стойности по подразбиране, което прави launch удобен за типични сценарии.

kotlin
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job

Параметърът context приема CoroutineContext, който комбинира диспечера, обработчика на изключения и именуван елемент. Най-често се предават Dispatchers.IO, Dispatchers.Main или SupervisorJob за изолиране на грешки.

Job и управление на жизнения цикъл

Job — върнатият обект, който представлява жизнения цикъл на корутината. Job може да бъде в състояния: New, Active, Completing, Completed, Cancelling, Cancelled. Всяко състояние отразява статуса на изпълнение и позволява реакция на промени.

kotlin
val job = CoroutineScope(Dispatchers.IO).launch {
    repeat(10) { i ->
        delay(1000L)
        println("Напредък: $i")
    }
}

println("Job е активен: ${job.isActive}")
delay(2500L)
job.cancel()
println("Job е отменен: ${job.isCancelled}")

Job поддържа йерархия: ако родителската корутина бъде отменена, всички дъщерни се отменят автоматично. SupervisorJob променя това поведение — дъщерните корутини не се отменят при грешка на една от тях.

Структурирана конкурентност в launch

Структурирана конкурентност означава, че всяка корутина се стартира в определен CoroutineScope и scope не завършва, докато всички дъщерни корутини не приключат работата си. launch напълно поддържа този принцип — корутините, стартирани вътре в друга корутина, са нейни деца.

Вложени launch повиквания

При вложено стартиране на launch се образува дърво от корутини. Родителската корутина изчаква завършването на всички деца, което гарантира предвидим ред на изпълнение и опростява управлението на ресурсите.

kotlin
fun main() = runBlocking {
    launch {
        launch {
            delay(1000L)
            println("Дете 1 завършено")
        }
        launch {
            delay(500L)
            println("Дете 2 завършено")
        }
        println("Всички деца завършени преди този ред")
    }
}

Това поведение се различава от конкурентността, базирана на нишки, където дъщерните нишки не са обвързани с родителската. При корутините родителят не завършва, докато всички деца не приключат работата си.

Обработка на изключения в launch

Обработката на изключения в launch зависи от типа на Job. За обикновен Job изключенията се разпространяват към родителската корутина и я отменят. За SupervisorJob или SupervisorScope изключенията са изолирани — грешката на една дъщерна корутина не засяга останалите.

kotlin
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
    try {
        riskyOperation()
    } catch (e: Exception) {
        println("Уловено: ${e.message}")
    }
}
scope.launch {
    println("Това все още работи благодарение на SupervisorJob")
}

За глобална обработка на неуловени изключения се използва CoroutineExceptionHandler. Той улавя изключения, които не са били обработени вътре в корутината, и позволява регистриране на грешката без срив на приложението.

Примери за използване на launch в Android

В Android launch се използва навсякъде: от ViewModel до WorkManager. Основният модел е използването на lifecycleScope във Fragment и viewModelScope в ViewModel за автоматично отменяне на корутини при завършване на жизнения цикъл на компонента.

kotlin
class ProfileViewModel : ViewModel() {
    private val repository = UserRepository()

    fun loadProfile(userId: String) {
        viewModelScope.launch(Dispatchers.IO) {
            val profile = repository.fetchProfile(userId)
            withContext(Dispatchers.Main) {
                _profileState.update { it.copy(profile = profile) }
            }
        }
    }
}

viewModelScope автоматично отменя корутините при унищожаване на ViewModel. Това елиминира изтичания на памет и гарантира, че фоновите операции не продължават след загуба на контекст. Допълнително може да се използва withContext за превключване между диспечери.

Често задавани въпроси

С какво се различава launch от async в Kotlin Coroutines?

launch връща Job и не връща резултат от изпълнението, докато async връща Deferred<T> за получаване на резултат. launch се използва за fire-and-forget операции, async — когато трябва да изчакате и използвате върнатата стойност.

Как да отменя корутина, стартирана чрез launch?

Извикайте job.cancel() на запазения обект Job. За групово отменяне отменете целия CoroutineScope чрез scope.cancel(). Корутината трябва да бъде кооперативна — да проверява isActive или да използва проверяеми suspend функции.

Какво да направите, ако launch е хвърлил изключение?

Използвайте try-catch вътре в launch блока или предайте CoroutineExceptionHandler в CoroutineContext. За изолиране на грешки използвайте SupervisorJob — тогава изключение в една дъщерна корутина няма да отмени останалите.

Може ли launch да се стартира без CoroutineScope?

Не, launch е разширяваща функция на CoroutineScope. Без scope корутината няма да може да следва принципа на структурирана конкурентност. Използвайте GlobalScope.launch внимателно — това създава корутина без обвързване с жизнения цикъл.

Колко launch корутини могат да бъдат стартирани едновременно?

Броят е ограничен от пула от нишки на диспечера. Dispatchers.Default използва брой нишки, равен на броя на CPU ядрата. Dispatchers.IO поддържа до 64 нишки. При стартиране на повече корутини те се поставят на опашка.

Резюме

  • launch — основен builder за fire-and-forget корутини, връщащ обект Job
  • Job предоставя методи за управление: cancel(), join(), isActive, isCancelled, isCompleted
  • Структурирана конкурентност гарантира отменяне на дъщерни корутини при отменяне на родителската
  • Обработка на изключения зависи от типа Job: обикновеният разпространява грешки, SupervisorJob изолира
  • lifecycleScope и viewModelScope — готови решения за стартиране на launch в Android
  • CoroutineStart.LAZY отлага стартирането до изрично извикване на start() или join()
  • launch в Android се прилага за мрежови заявки, запис в БД, аналитика и кеширане

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също