programowanieKotlin Software Engineer

Jak działają korutyny w Kotlinie? Opisz mechanizm uruchamiania, dyspozycję, anulowanie i obsługę błędów. Podaj przykład użycia i wyjaśnij niuanse związane z przekazywaniem kontekstu i wyjątkami.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Korutyny w Kotlinie to lekkie wątki wspierające wstrzymywanie i wznawianie obliczeń bez blokowania wątków OS. Są uruchamiane przez funkcje takie jak launch, async w obecności nadrzędnego CoroutineScope.

Dyspozycja odbywa się za pomocą obiektów CoroutineDispatcher, które określają, w którym wątku/wykonawcy realizowana jest korutyna (Dispatchers.Main, Dispatchers.IO, Dispatchers.Default).

Przykład:

fun main() = runBlocking { launch(Dispatchers.IO) { val data = getDataFromNetwork() withContext(Dispatchers.Main) { updateUI(data) } } }

Anulowanie: Korutyny są anulowane przez anulowanie ich job/Scope, co powoduje wyrzucenie wewnętrznego wyjątku CancellationException. Korutyny powinny okresowo sprawdzać flagę anulowania lub wywoływać funkcje wstrzymujące, aby poprawnie zakończyć działanie.

Obsługa błędów: Wyjątki w korutynach mogą "znikać" — na przykład, jeśli nie obsłużysz błędu w podrzędnej korutynie, zakończy ona tylko swoją pracę, a nadrzędny nie dowie się o problemie. Istnieją mechanizmy SupervisorJob i CoroutineExceptionHandler w takim przypadku.

Niuansy przekazywania kontekstu:

  • Kontekst (np. Job, Dispatcher, elementy użytkownika) jest dziedziczony przy tworzeniu nowych korutyn.
  • Nie można po prostu przekazać parametrów między scope'ami, jeśli te są zamknięte lub anulowane.

Pytanie z pułapką

Co się stanie, jeśli w obrębie nadrzędnej korutyny jedna z podrzędnych zakończy się błędem, a inne będą kontynuować pracę? Czy wszystkie podrzędne zostaną anulowane?

Wielu błędnie uważa, że błąd "pozostaje w podrzędnej".

Prawidłowa odpowiedź: Jeśli używany jest zwykły Job (lub launch), to wszystkie podrzędne korutyny są automatycznie anulowane przy błędzie jakiejkolwiek podrzędnej. Aby podrzędne korutyny nie były anulowane, używa się SupervisorJob lub supervisorScope:

supervisorScope { launch { error("fail") } launch { println("Ten kod będzie działał") } }

Przykłady rzeczywistych błędów wynikających z braku znajomości tematu


Historia

Nieprawidłowa obsługa błędów — awaria flow: W projekcie używano korutyn do ładowania danych. Wyjątki "tonęły" wewnątrz podrzędnych korutyn, przez co poziom retry/recovery nie działał, a główny ekran pozostawał pusty bez żadnych komunikatów o błędach dla użytkownika.


Historia

Zawieszenie UI z powodu pracy na Dispatchers.Main: Młody programista Androida uruchomił ciężkie obliczenia na Dispatchers.Main — UI zaczął "zamrażać". Nie było zrozumienia, że wszelkie ciężkie obliczenia powinny być realizowane na Dispatchers.Default lub Dispatchers.IO.


Historia

AbortError przy anulowaniu nadrzędnego scope: Jeden z programistów nie wziął pod uwagę, że przy anulowaniu nadrzędnego scope wszystkie podrzędne korutyny kończą się anulowaniem, a krytyczna operacja (zapisywanie danych) nie została zakończona.