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("Progress: $i")
}
}
println("Job is active: ${job.isActive}")
delay(2500L)
job.cancel()
println("Job is cancelled: ${job.isCancelled}")
Job поддерживает иерархию: если родительская корутина отменяется, все дочерние автоматически отменяются. SupervisorJob изменяет это поведение — дочерние корутины не отменяются при ошибке одной из них.
Структурированная конкурентность означает, что каждая корутина запускается в определённом CoroutineScope, и scope не завершится, пока все дочерние корутины не закончат работу. launch полностью поддерживает этот принцип — корутины, запущенные внутри другой корутины, являются её дочерними.
При вложенном запуске launch образуется дерево корутин. Родительская корутина ожидает завершения всех дочерних, что гарантирует предсказуемый порядок выполнения и упрощает управление ресурсами.
fun main() = runBlocking {
launch {
launch {
delay(1000L)
println("Child 1 completed")
}
launch {
delay(500L)
println("Child 2 completed")
}
println("All children complete before this line")
}
}
Это поведение отличается от thread-based конкурентности, где дочерние потоки не привязаны к родительскому. В корутинах родитель не завершится, пока все дети не закончат работу.
Обработка исключений в launch зависит от типа Job. Для обычного Job исключения распространяются на родительскую корутину и отменяют её. Для SupervisorJob или SupervisorScope исключения изолируются — ошибка одной дочерней корутины не затрагивает другие.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try {
riskyOperation()
} catch (e: Exception) {
println("Caught: ${e.message}")
}
}
scope.launch {
println("This still runs thanks to 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также