launch — Coroutine Builder в Kotlin, който стартира корутина без връщане на резултат, връщайки обект Job за управление на изпълнението. Този builder имплементира модела fire-and-forget: корутината започва работа във фонова нишка и автоматично завършва. Според документацията на JetBrains, 2024, launch е основният начин за стартиране на паралелни задачи в Kotlin Coroutines.
Основни точки
launch — е разширяваща функция на Kotlin, достъпна чрез import kotlinx.coroutines, която създава нова корутина в зададен CoroutineScope. За разлика от обикновените функции, кодът вътре в launch се изпълнява конкурентно — корутината може да бъде спряна (suspend) без блокиране на нишката и да бъде възобновена по-късно.
Launch builder е фундаментален градивен блок на модела на корутините в Kotlin. Той не връща изчислена стойност, но връща обект Job, който позволява контрол на изпълнението на корутината. Всички дъщерни корутини, стартирани чрез launch вътре в друга корутина, са обвързани с родителската.
Launch builder е дефиниран като inline функция с няколко параметъра: CoroutineScope, CoroutineContext, CoroutineStart и suspend-блок. Параметрите имат стойности по подразбиране, което прави launch удобен за типични сценарии.
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 може да бъде в състояния: New, Active, Completing, Completed, Cancelling, Cancelled. Всяко състояние отразява статуса на изпълнение и позволява реакция на промени.
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 променя това поведение — дъщерните корутини не се отменят при грешка на една от тях.
Структурирана конкурентност означава, че всяка корутина се стартира в определен CoroutineScope и scope не завършва, докато всички дъщерни корутини не приключат работата си. launch напълно поддържа този принцип — корутините, стартирани вътре в друга корутина, са нейни деца.
При вложено стартиране на launch се образува дърво от корутини. Родителската корутина изчаква завършването на всички деца, което гарантира предвидим ред на изпълнение и опростява управлението на ресурсите.
fun main() = runBlocking {
launch {
launch {
delay(1000L)
println("Дете 1 завършено")
}
launch {
delay(500L)
println("Дете 2 завършено")
}
println("Всички деца завършени преди този ред")
}
}
Това поведение се различава от конкурентността, базирана на нишки, където дъщерните нишки не са обвързани с родителската. При корутините родителят не завършва, докато всички деца не приключат работата си.
Обработката на изключения в launch зависи от типа на Job. За обикновен Job изключенията се разпространяват към родителската корутина и я отменят. За SupervisorJob или SupervisorScope изключенията са изолирани — грешката на една дъщерна корутина не засяга останалите.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try {
riskyOperation()
} catch (e: Exception) {
println("Уловено: ${e.message}")
}
}
scope.launch {
println("Това все още работи благодарение на SupervisorJob")
}
За глобална обработка на неуловени изключения се използва CoroutineExceptionHandler. Той улавя изключения, които не са били обработени вътре в корутината, и позволява регистриране на грешката без срив на приложението.
В Android launch се използва навсякъде: от ViewModel до WorkManager. Основният модел е използването на lifecycleScope във Fragment и viewModelScope в ViewModel за автоматично отменяне на корутини при завършване на жизнения цикъл на компонента.
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 връща Job и не връща резултат от изпълнението, докато async връща Deferred<T> за получаване на резултат. launch се използва за fire-and-forget операции, async — когато трябва да изчакате и използвате върнатата стойност.
Извикайте job.cancel() на запазения обект Job. За групово отменяне отменете целия CoroutineScope чрез scope.cancel(). Корутината трябва да бъде кооперативна — да проверява isActive или да използва проверяеми suspend функции.
Използвайте try-catch вътре в launch блока или предайте CoroutineExceptionHandler в CoroutineContext. За изолиране на грешки използвайте SupervisorJob — тогава изключение в една дъщерна корутина няма да отмени останалите.
Не, launch е разширяваща функция на CoroutineScope. Без scope корутината няма да може да следва принципа на структурирана конкурентност. Използвайте GlobalScope.launch внимателно — това създава корутина без обвързване с жизнения цикъл.
Броят е ограничен от пула от нишки на диспечера. Dispatchers.Default използва брой нишки, равен на броя на CPU ядрата. Dispatchers.IO поддържа до 64 нишки. При стартиране на повече корутини те се поставят на опашка.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също