launch — Coroutine Builder у Kotlin, який запускає корутину без повернення результату, повертаючи об'єкт Job для керування виконанням. Цей білдер реалізує патерн fire-and-forget: корутина починає роботу у фоновому потоці й автоматично завершується. За даними документації JetBrains, 2024, launch є основним способом запуску паралельних завдань у Kotlin Coroutines.
Головне
launch — це функція-розширення Kotlin, доступна через імпорт kotlinx.coroutines, яка створює нову корутину в заданому CoroutineScope. На відміну від звичайних функцій, код всередині launch виконується конкурентно — корутина може призупинятися (suspend) без блокування потоку та відновлюватися пізніше.
Білдер launch є фундаментальним будівельним блоком корутинної моделі Kotlin. Він не повертає обчислене значення, але повертає об'єкт Job, який дозволяє контролювати виконання корутини. Усі дочірні корутини, запущені через launch всередині іншої корутини, прив'язуються до батьківської.
Білдер launch визначається як 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("Усі дочірні завершуються до цього рядка")
}
}
Ця поведінка відрізняється від thread-based конкурентності, де дочірні потоки не прив'язані до батьківського. У корутинах батько не завершується, доки всі діти не закінчать роботу.
Обробка винятків у 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також