Coroutine Builder — funkcje Kotlin Coroutines, które tworzą i uruchamiają korytyny, określając sposób ich wykonania. Builderzy launch, async, runBlocking i produce pokrywają różne scenariusze: od zadań w tle po równoległe obliczenia ze zwracaniem wyniku. Według danych JetBrains, 2024, Coroutine Builder jest podstawą modelu korytyn, zapewniając ustrukturyzowaną współbieżność i zarządzanie cyklem życia.
Najważniejsze
Coroutine Builder — to funkcja rozszerzająca Kotlin, która przyjmuje CoroutineScope i suspend-blok, tworząc i uruchamiając nową korytynę. Każdy builder określa, jak korytyna będzie wykonywana: ze zwracaniem wyniku lub bez, z blokowaniem wątku lub asynchronicznie. Builderzy są punktami wejścia do modelu korytyn w języku.
Wszystkie builderzy działają przez CoroutineScope, który zarządza cyklem życia korytyn potomnych. Po anulowaniu scope wszystkie uruchomione przez niego korytyny są automatycznie anulowane — to zasada ustrukturyzowanej współbieżności. Takie podejście zapobiega wyciekom korytyn i gwarantuje przewidywalne zakończenie.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Builderzy działają wewnątrz CoroutineScope
val job = launch {
delay(1000L)
println("Świecie!")
}
println("Cześć,")
job.join()
}
Kotlin udostępnia cztery wbudowane builderzy korytyn: launch, async, runBlocking i produce. Każdy z nich ma swój typ zwracany i zakres zastosowania. Do programowania mobilnego na Android głównymi są launch i async — działają w sposób nieblokujący i integrują się z komponentami architektonicznymi.
| Builder | Typ zwracany | Blokowanie wątku | Scenariusz |
|---|---|---|---|
| launch | Job | Nie | Zadania fire-and-forget |
| async | Deferred<T> | Nie | Obliczenia równoległe |
| runBlocking | T | Tak | Testy, funkcja main |
| produce | ReceiveChannel<E> | Nie | Przesyłanie strumieniowe (deprecated) |
Każdy builder przyjmuje dodatkowe parametry: CoroutineStart (strategia uruchamiania), CoroutineContext (dyspozytor, wyjątki) i nazwany blok kodu. Domyślnie korytyna uruchamiana jest natychmiast (CoroutineStart.DEFAULT).
launch — najczęściej używany builder w programowaniu na Androida. Uruchamia korytynę, która nie zwraca wyniku, i zwraca obiekt Job do zarządzania jej cyklem życia. To idealny wybór dla operacji, gdzie potrzebny jest tylko efekt uboczny: zapis do bazy danych, wysyłka analityki, aktualizacja UI.
Builder launch przyjmuje CoroutineScope, opcjonalny CoroutineContext i suspend-blok. Zwracany Job pozwala anulować korytynę, czekać na jej zakończenie lub sprawdzić status.
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
val data = fetchFromNetwork()
saveToDatabase(data)
}
job.start()
job.join()
Parametr CoroutineStart.LAZY opóźnia uruchomienie do jawnego wywołania start() lub join(). Jest to przydatne do opóźnionej inicjalizacji i warunkowego uruchamiania. Do standardowego natychmiastowego uruchomienia używany jest CoroutineStart.DEFAULT lub parametr jest pomijany.
async — builder zwracający Deferred<T> — asynchroniczną obietnicę wyniku. Wywołanie await() wstrzymuje korytynę do uzyskania wyniku, nie blokując wątku. To podstawowy mechanizm dla zadań równoległych w korytynach Kotlin.
async jest szczególnie efektywny, gdy trzeba wykonać kilka niezależnych operacji jednocześnie. W przeciwieństwie do sekwencyjnego wywoływania suspend-funkcji, async uruchamia korytyny równolegle, skracając całkowity czas wykonania.
suspend fun fetchUserData(): UserData {
val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
return UserData(
profile = deferred1.await(),
settings = deferred2.await(),
notifications = deferred3.await()
)
}
Deferred dziedziczy po Job, więc async obsługuje wszystkie operacje cyklu życia: anulowanie, oczekiwanie na zakończenie, obsługę wyjątków. Po anulowaniu scope korytyny potomne Deferred są anulowane automatycznie.
runBlocking — jedyny builder, który blokuje bieżący wątek do zakończenia korytyny. Tworzy nowy CoroutineScope i uruchamia przekazaną korytynę, blokując wywołujący wątek. Używany w punktach wejścia main(), w testach i przy integracji z blocking-kodem.
runBlocking jest uzasadniony w trzech scenariuszach: punkt wejścia aplikacji (main), testy jednostkowe suspend-funkcji oraz integracja z bibliotekami callback-based, gdzie nie można użyć suspend. W kodzie produkcyjnym Androida używanie runBlocking na głównym wątku kategorycznie nie jest zalecane.
class CoroutineTest {
@Test
fun `test suspend function`() = runBlocking {
val result = mySuspendFunction()
assertEquals("expected", result)
}
}
Do testów zaleca się używanie kotlinx-coroutines-test z TestCoroutineDispatcher zamiast runBlocking — zapewnia to kontrolę nad czasem i unika blokad w środowisku testowym.
Wybór Coroutine Builder zależy od zwracanego wyniku i scenariusza wykonania. Jeśli operacja nie wymaga zwracania danych — użyj launch. Jeśli potrzebny jest wynik operacji asynchronicznej — async. runBlocking stosuj tylko do bridging, a produce zastąp Flow dla strumieni reaktywnych.
W projektach Android używających Kotlin Coroutines główną parą builderów są launch i async. launch jest używany w ViewModel i UseCases do uruchamiania korytyn, a async — do równoległych żądań do sieci lub bazy danych. Nowoczesne biblioteki (Ktor, Room) już obsługują suspend-funkcje, co minimalizuje potrzebę bezpośredniego używania async.
Często zadawane pytania
launch zwraca Job i nie zwraca wyniku wykonania, a async zwraca Deferred<T> — obiekt, z którego można uzyskać wynik przez await(). launch jest używany do operacji fire-and-forget, async — do zadań zwracających dane.
Nie jest zalecane. runBlocking na głównym wątku powoduje ANR i blokuje UI. Używaj lifecycleScope.launch wewnątrz Activity i Fragment — to wbudowane rozwiązanie bez blokad.
Builder launch zwraca obiekt Job, który pozwala kontrolować cykl życia korytyny: anulować (cancel), czekać na zakończenie (join), sprawdzić status (isActive, isCompleted, isCancelled).
Deferred<T> — to asynchroniczna obietnica wyniku, zwracana przez builder async. Dziedziczy po Job i dodaje metody await() do uzyskania wyniku, getCompleted() do nieblokującego dostępu oraz getCompletionExceptionOrNull() do sprawdzania wyjątku.
Użyj parametru CoroutineStart.LAZY: scope.launch(start = CoroutineStart.LAZY) { ... }. Następnie wywołaj job.start() lub job.join() do faktycznego uruchomienia. Jest to przydatne do opóźnionej inicjalizacji i warunkowego wykonywania korytyn.
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ż