CoroutineScope — este o interfață Kotlin care definește domeniul de viață al corutinei și oferă context pentru lansarea de noi corutine. Potrivit documentației Kotlin, 2025, fiecare instanță CoroutineScope conține CoroutineContext și gestionează toate corutinele lansate în ea. Când scope se încheie (cancel), toate corutinele copil sunt anulate automat, prevenind scurgerile de memorie.
Puncte principale
CoroutineScope — este o interfață fundamentală din biblioteca kotlinx.coroutines care servește ca container pentru corutine. Aceasta definește limitele de viață ale corutinelor: când scope se încheie, toate corutinele din interior sunt anulate automat.
public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}
Interfața conține un singur câmp — coroutineContext. Prin intermediul acestuia, scope furnizează dispatcher (Dispatcher), sarcină (Job), gestionarea excepțiilor și alte elemente de context pentru toate corutinele lansate în el.
Toate funcțiile de lansare a corutinelor — launch, async, runBlocking — sunt funcții de extensie pe CoroutineScope. Aceasta înseamnă că pot fi apelate doar în prezența unui obiect scope. Un astfel de design garantează că fiecare corutină are un părinte și un ciclu de viață clar definite.
În Android, fiecare componentă arhitecturală are propriul scope: viewModelScope pentru ViewModel, lifecycleScope pentru Activity/Fragment. În aplicațiile server, scope poate fi legat de o cerere HTTP sau de un pool de conexiuni cu baza de date.
Înțelegerea structurii interne a CoroutineScope necesită familiarizarea cu conceptul de Job și principiul concurenței structurale.
Fiecare corutină la lansare returnează un obiect Job (sau Deferred pentru async). Job reprezintă o sarcină cu un ciclu de viață definit: New, Active, Completing, Completed, Cancelling, Cancelled. Obiectele Job formează o structură arborescentă:
Concurența structurală — principiul arhitectural cheie al Kotlin Coroutines, în care durata de viață a corutinei este legată de durata de viață a scope-ului său. Acesta contrastează cu modelul „fire-and-forget”, unde corutina continuă să existe după încheierea scope-ului. Avantajele concurenței structurale:
Când este apelat scope.cancel(), Job-ul scope-ului trece în starea Cancelled, ceea ce anulează recursiv toate Job-urile copil. După anulare, scope poate fi reutilizat doar prin crearea unei noi instanțe CoroutineScope.
CoroutineScope poate fi creat printr-o funcție fabrică sau prin implementarea interfeței în propria clasă. Să analizăm ambele abordări.
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
scope.launch {
println("Rulează pe ${Thread.currentThread().name}")
}
Funcția fabrică primește CoroutineContext și creează scope cu contextul specificat. În exemplu se utilizează Dispatchers.Default pentru sarcini CPU-intensive și SupervisorJob care izolează excepțiile între corutinele copil.
class MyRepository {
private val scope = CoroutineScope(Dispatchers.IO + Job())
suspend fun fetchData(): Data = scope.async {
api.getData()
}.await()
fun cleanup() {
scope.cancel()
}
}
Stocăm scope ca câmp al clasei și apelăm manual cleanup pentru anularea acestuia. Aceasta este potrivit pentru componente cu ciclu de viață gestionat — de exemplu, pentru repository-uri sau manageri.
Kotlin permite delegarea implementării CoroutineScope prin cuvântul cheie by:
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
fun load() {
launch {
// corutina rulează în scope-ul DataLoader
}
}
}
Această abordare este convenabilă atunci când clasa înseși este scope și dorește să ofere metode de lansare a corutinelor. Cu toate acestea, fiți precauți: clasa moștenește toate metodele CoroutineScope, inclusiv cancel, ceea ce poate încălca encapsularea.
GlobalScope — este un singleton CoroutineScope pentru întreaga aplicație. Utilizarea sa în codul de producție nu este recomandată oficial.
JetBrains permite GlobalScope doar în scenarii rare: procese de fundal la nivelul aplicației care trebuie să trăiască chiar și după închiderea tuturor Activity (de exemplu, sincronizarea datelor, analitica). Dar și în aceste cazuri este preferabil să creați propriul scope cu CoroutineScope(SupervisorJob()).
Utilizați întotdeauna CoroutineScope personalizat cu gestionare explicită a ciclului de viață. În Android, acestea sunt viewModelScope și lifecycleScope. În aplicațiile server, creați scope pentru fiecare cerere sau pool de conexiuni.
Ambele funcții sunt funcții suspend care creează un scope temporar pentru sarcini paralele, dar comportamentul lor la excepții diferă fundamental.
| Caracteristică | coroutineScope | supervisorScope |
|---|---|---|
| Comportament la eroare | Excepția în corutina copil anulează toate celelalte | Excepția în corutina copil NU anulează celelalte |
| Propagarea erorii | Da, prima excepție este propagată în exterior | Da, prima excepție este propagată în exterior |
| Job implicit | Job() — copiii sunt legați de părinte | SupervisorJob() — copiii nu depind unii de alții |
| Caz tipic de utilizare | Operație atomică din mai mulți pași | Sarcini paralele independente (încărcări UI) |
Utilizați coroutineScope atunci când mai multe operațiuni paralele formează o singură operație atomică. De exemplu, încărcarea datelor de pe trei servere: dacă o cerere eșuează, celelalte nu au sens.
suspend fun loadProductPage(): ProductPage = coroutineScope {
val product = async { api.getProduct() }
val reviews = async { api.getReviews() }
ProductPage(product.await(), reviews.await())
}
Dacă getProduct sau getReviews aruncă o excepție — ambele corutine sunt anulate, iar excepția este propagată codului apelant.
Utilizați supervisorScope atunci când operațiunile paralele nu depind unele de altele. De exemplu, încărcarea datelor de profil în mai multe secțiuni independente: dacă secțiunea de recomandări eșuează, antetul profilului și lista de prieteni ar trebui să se afișeze.
Să analizăm cele mai frecvente erori ale dezvoltatorilor la utilizarea CoroutineScope în Kotlin.
Cel mai frecvent scenariu de scurgere a corutinei — crearea scope fără a apela cancel la finalizarea componentei. Dacă scope nu este anulat, corutinele continuă să ruleze, reținând referințe la obiecte. În Android, utilizați viewModelScope sau lifecycleScope, care sunt anulate automat.
GlobalScope ignoră ciclul de viață al componentelor Android. O corutină lansată în GlobalScope după închiderea Activity va continua să ruleze și să încearce să actualizeze UI — ceea ce va duce la un crash. Utilizați întotdeauna lifecycleScope pentru componentele UI.
După apelarea cancel(), scope nu poate fi reutilizat — toate corutinele din el sunt deja finalizate. Creați o nouă instanță CoroutineScope prin funcția fabrică. Job() nu suportă reactivarea.
La delegarea prin by, clasa primește metoda publică cancel() care poate fi apelată din orice loc, încălcând encapsularea. Păstrați scope ca un câmp privat, nu delegați interfața.
Întrebări frecvente
CoroutineScope — este o interfață care deține CoroutineContext și este responsabilă pentru ciclul de viață al corutinelor. CoroutineContext — este un set de elemente (dispatcher, job, gestionarea erorilor) care definește „cum” se execută corutina. Una dintre diferențe: scope creează corutine, context gestionează comportamentul lor.
Da, acesta este un model standard: CoroutineScope(Dispatchers.IO + SupervisorJob()). SupervisorJob previne anularea în cascadă a corutinelor copil la o excepție într-una dintre ele. Acest lucru este util pentru sarcini paralele independente, unde o eroare într-una nu ar trebui să le oprească pe celelalte.
Nu există limitări privind numărul de corutine într-un scope — ele sunt limitate doar de memoria disponibilă și setările dispatcher-ului. Limita practică este de obicei de mii de corutine active într-un singur scope. Cu toate acestea, un număr mare de corutine poate indica probleme arhitecturale.
Modul corect este să transmiteți scope clasei prin constructor sau să utilizați runBlockingTest / runTest din kotlinx-coroutines-test. În teste, puteți înlocui scope cu TestCoroutineDispatcher și controla manual execuția corutinelor.
Nu, scope este un container extern pentru corutină. Corutina în sine nu este un scope. Cu toate acestea, în interiorul unei corutine se poate crea un nou scope prin coroutineScope sau supervisorScope pentru lansarea paralelă a corutinelor copil.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și