SupervisorJob a Job: izolowanie błędów
Użyj SupervisorJob, aby błąd jednego zadania podrzędnego nie anulował jego rodzeństwa.
SupervisorJob a Job: izolowanie błędów to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.
Domyślne propagowanie błędów Job
W przypadku zwykłego obiektu Job błąd dziecka anuluje rodzica, który następnie anuluje wszystkie elementy równorzędne. Jeden błąd powoduje przerwanie całej hierarchii.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
coroutineScope {
launch { delay(50); throw RuntimeException("child 1 failed") }
launch { delay(1000); println("child 2 — never prints") }
}
} catch (e: RuntimeException) {
println("Caught: ${e.message}")
}
}Przegląd SupervisorJob
SupervisorJob zmienia tę zasadę: błąd dziecka NIE anuluje elementów równorzędnych ani rodzica. Każde dziecko kończy się błędem niezależnie.
import kotlinx.coroutines.*
fun main() = runBlocking {
val supervisor = SupervisorJob()
val scope = CoroutineScope(coroutineContext + supervisor)
scope.launch { throw RuntimeException("child 1 failed") }
scope.launch { delay(100); println("child 2 still runs") }
delay(200)
supervisor.cancel()
}Konstruktor supervisorScope
supervisorScope { } tworzy zakres z obiektem SupervisorJob jako rodzicem. Jest to idiomatyczny sposób uruchamiania niezależnych elementów potomnych.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
val job1 = launch {
throw RuntimeException("job1 failed")
}
val job2 = launch {
delay(100)
println("job2 succeeded")
}
job1.join() // wait for job1 (it failed)
job2.join() // job2 is unaffected
}
}Przechwytywanie błędów dzieci
W supervisorScope każde dziecko obsługuje własny wyjątek. Nieobsłużone wyjątki w dzieciach należy przechwytywać osobno dla każdego dziecka.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
val result = async {
delay(50)
throw RuntimeException("async failed")
}
try {
result.await()
} catch (e: RuntimeException) {
println("Caught from async: ${e.message}")
}
println("Scope continues")
}
}Job a SupervisorJob podczas tworzenia zakresu
Przekaż SupervisorJob() podczas tworzenia CoroutineScope dla usługi lub ViewModelu, który powinien działać mimo pojedynczych błędów dzieci.
import kotlinx.coroutines.*
class MyService {
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
fun startTask(name: String) {
scope.launch {
if (name == "fail") throw RuntimeException("$name failed")
println("$name done")
}
}
fun stop() = scope.cancel()
}
fun main() = runBlocking {
val svc = MyService()
svc.startTask("fail")
svc.startTask("ok")
delay(100)
svc.stop()
}viewModelScope używa SupervisorJob
Androidowy zakres viewModelScope korzysta z obiektu SupervisorJob. Jedno nieudane uruchomienie nie anuluje pozostałych korutyn ViewModelu.
import kotlinx.coroutines.*
// Android ViewModel internals:
// val viewModelScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
// Each launch is independent — one failure doesn't crash the ViewModel
fun main() = runBlocking {
println("viewModelScope = SupervisorJob + Main")
}Dekompzycja równoległa z użyciem supervisorScope
Należy użyć supervisorScope + async do zadań wykonywanych równolegle, w których niektóre mogą zakończyć się niepowodzeniem bez wpływu na pozostałe.
import kotlinx.coroutines.*
fun main() = runBlocking {
val results = supervisorScope {
val a = async { delay(50); "result-A" }
val b = async { throw RuntimeException("B failed") }
val c = async { delay(30); "result-C" }
listOf(
runCatching { a.await() },
runCatching { b.await() },
runCatching { c.await() }
)
}
results.forEach { println(it) }
}Kiedy używać zwykłego Job
Należy użyć zwykłego Job (coroutineScope), gdy wszystkie korutyny potomne muszą zakończyć się pomyślnie razem — jedno niepowodzenie powinno przerwać całą grupę. To jak transakcja: wszystko albo nic.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
coroutineScope { // regular Job
val a = async { delay(50); "A" }
val b = async { throw RuntimeException("B failed") }
println(a.await())
println(b.await()) // throws — cancels a too
}
} catch (e: RuntimeException) {
println("Transaction failed: ${e.message}")
}
}Propagacja wyjątków w supervisorScope
W supervisorScope wyjątek propaguje do zakresu tylko wtedy, gdy wydostanie się z najwyższego poziomu korutyny potomnej — tzn. gdy w korutynie potomnej nie ma try-catch. Korutyna nadrzędna go nie otrzymuje.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
// Exception caught inside child — scope unaffected
launch {
try { throw RuntimeException("handled") }
catch (e: Exception) { println("Child caught: ${e.message}") }
}
delay(100)
println("Scope survived")
}
}CoroutineExceptionHandler z SupervisorJob
Należy umieścić CoroutineExceptionHandler w zakresie typu supervisor, aby rejestrować nieprzechwycone błędy korutyn potomnych lub na nie reagować bez powodowania awarii zakresu.
import kotlinx.coroutines.*
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, e ->
println("Uncaught: ${e.message}")
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch { throw RuntimeException("unhandled failure") }
scope.launch { delay(100); println("still running") }
delay(200)
scope.cancel()
}Podsumowanie izolacji niepowodzeń
Job: jedno dziecko kończy się niepowodzeniem → wszystkie kończą się niepowodzeniem. SupervisorJob: dzieci kończą się niepowodzeniem niezależnie od siebie. Mechanizmu supervisor należy używać w usługach i ViewModels, a zwykłego Job w grupach transakcyjnych.
import kotlinx.coroutines.*
// Decision table:
// coroutineScope { } -> Job: all-or-nothing
// supervisorScope { } -> SupervisorJob: independent failures
// CoroutineScope(SupervisorJob()) -> long-lived service
fun main() = runBlocking { println("Pick the right job for the right scope") }Szybkie sprawdzenie
Który zakres zapobiega anulowaniu rodzeństwa po niepowodzeniu korutyny potomnej?
Podsumowanie
Job: niepowodzenie propaguje się do rodzeństwa. SupervisorJob/supervisorScope: niepowodzenia są izolowane dla każdej korutyny potomnej. Należy używać supervisorScope do niezależnych zadań wykonywanych równolegle, a zwykłego coroutineScope w grupach transakcyjnych.
Często zadawane pytania
Czy lekcja „SupervisorJob a Job: izolowanie błędów” jest bezpłatna?
Tak — pełny tekst „SupervisorJob a Job: izolowanie błędów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „SupervisorJob a Job: izolowanie błędów”?
Użyj SupervisorJob, aby błąd jednego zadania podrzędnego nie anulował jego rodzeństwa. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?
Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „SupervisorJob a Job: izolowanie błędów”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?
Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- SupervisorJob a Job: izolowanie błędów
- CoroutineExceptionHandler: globalny handler nieprzechwyconych wyjątków
- Propagacja wyjątków w async/await
- Projektowanie odpornych architektur korutyn