launch — Coroutine Builder w Kotlin, który uruchamia korutynę bez zwracania wyniku, zwracając obiekt Job do zarządzania wykonaniem. Ten builder implementuje wzorzec fire-and-forget: korutyna rozpoczyna pracę w wątku tła i automatycznie się kończy. Według dokumentacji JetBrains, 2024, launch jest głównym sposobem uruchamiania zadań równoległych w Kotlin Coroutines.
Najważniejsze
launch — to funkcja rozszerzająca Kotlin, dostępna przez import kotlinx.coroutines, która tworzy nową korutynę w zadanym CoroutineScope. W przeciwieństwie do zwykłych funkcji, kod wewnątrz launch wykonuje się współbieżnie — korutyna może się zawieszać (suspend) bez blokowania wątku i wznawiać później.
Builder launch jest fundamentalnym blokiem konstrukcyjnym modelu korutyn w Kotlin. Nie zwraca obliczonej wartości, ale zwraca obiekt Job, umożliwiający kontrolowanie wykonania korutyny. Wszystkie korutyny potomne uruchomione przez launch wewnątrz innej korutyny są powiązane z rodzicielską.
Builder launch jest zdefiniowany jako funkcja inline z kilkoma parametrami: CoroutineScope, CoroutineContext, CoroutineStart i suspend-block. Parametry mają wartości domyślne, co czyni launch wygodnym dla typowych scenariuszy.
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
Parametr context przyjmuje CoroutineContext, łączący dyspozytora, obsługę wyjątków i nazwany element. Najczęściej przekazywany jest Dispatchers.IO, Dispatchers.Main lub SupervisorJob do izolacji błędów.
Job — zwracany obiekt reprezentujący cykl życia korutyny. Job może znajdować się w stanach: New, Active, Completing, Completed, Cancelling, Cancelled. Każdy stan odzwierciedla status wykonania i pozwala reagować na zmiany.
val job = CoroutineScope(Dispatchers.IO).launch {
repeat(10) { i ->
delay(1000L)
println("Postęp: $i")
}
}
println("Job jest aktywny: ${job.isActive}")
delay(2500L)
job.cancel()
println("Job jest anulowany: ${job.isCancelled}")
Job wspiera hierarchię: jeśli rodzicielska korutyna zostanie anulowana, wszystkie potomne są automatycznie anulowane. SupervisorJob zmienia to zachowanie — korutyny potomne nie są anulowane przy błędzie jednej z nich.
Ustrukturyzowana współbieżność oznacza, że każda korutyna jest uruchamiana w określonym CoroutineScope, a scope nie zakończy się, dopóki wszystkie korutyny potomne nie zakończą pracy. launch w pełni wspiera tę zasadę — korutyny uruchomione wewnątrz innej korutyny są jej potomnymi.
Przy zagnieżdżonym uruchamianiu launch powstaje drzewo korutyn. Rodzicielska korutyna oczekuje na zakończenie wszystkich potomnych, co gwarantuje przewidywalną kolejność wykonania i upraszcza zarządzanie zasobami.
fun main() = runBlocking {
launch {
launch {
delay(1000L)
println("Potomny 1 zakończony")
}
launch {
delay(500L)
println("Potomny 2 zakończony")
}
println("Wszystkie potomne zakończone przed tą linią")
}
}
To zachowanie różni się od współbieżności opartej na wątkach, gdzie wątki potomne nie są powiązane z rodzicielskim. W korutynach rodzic nie zakończy się, dopóki wszystkie dzieci nie zakończą pracy.
Obsługa wyjątków w launch zależy od typu Job. W przypadku zwykłego Job wyjątki rozprzestrzeniają się na rodzicielską korutynę i anulują ją. Dla SupervisorJob lub SupervisorScope wyjątki są izolowane — błąd jednej korutyny potomnej nie wpływa na pozostałe.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try {
riskyOperation()
} catch (e: Exception) {
println("Przechwycono: ${e.message}")
}
}
scope.launch {
println("To wciąż działa dzięki SupervisorJob")
}
Do globalnej obsługi nieprzechwyconych wyjątków używa się CoroutineExceptionHandler. Przechwytuje on wyjątki, które nie zostały obsłużone wewnątrz korutyny, i umożliwia zalogowanie błędu bez crasha aplikacji.
W Android launch jest używany wszechobecnie: od ViewModel do WorkManager. Główny wzorzec — użycie lifecycleScope we Fragment i viewModelScope w ViewModel do automatycznego anulowania korutyn przy zakończeniu cyklu życia komponentu.
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 automatycznie anuluje korutyny przy zniszczeniu ViewModel. Eliminuje to wycieki i gwarantuje, że operacje w tle nie są kontynuowane po utracie kontekstu. Dodatkowo można użyć withContext do przełączania między dyspozytorami.
Często zadawane pytania
launch zwraca Job i nie zwraca wyniku wykonania, a async zwraca Deferred<T> do uzyskania wyniku. launch jest używany do operacji fire-and-forget, async — gdy trzeba poczekać i użyć zwróconej wartości.
Wywołaj job.cancel() na zapisanym obiekcie Job. Do grupowego anulowania anuluj cały CoroutineScope przez scope.cancel(). Korutyna musi być kooperatywna — sprawdzać isActive lub używać sprawdzalnych funkcji suspend.
Użyj try-catch wewnątrz bloku launch lub przekaż CoroutineExceptionHandler w CoroutineContext. Do izolacji błędów użyj SupervisorJob — wtedy wyjątek w jednej korutynie potomnej nie anuluje pozostałych.
Nie, launch — funkcja rozszerzająca CoroutineScope. Bez scope korutyna nie będzie mogła przestrzegać zasady ustrukturyzowanej współbieżności. Używaj GlobalScope.launch ostrożnie — tworzy to korutynę bez powiązania z cyklem życia.
Ilość jest ograniczona pulą wątków dyspozytora. Dispatchers.Default używa liczby wątków równej liczbie rdzeni CPU. Dispatchers.IO obsługuje do 64 wątków. Przy uruchamianiu większej liczby korutyn są one kolejkowane.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również