CoroutineScope — ay isang interface ng Kotlin na tumutukoy sa saklaw ng buhay ng coroutine at nagbibigay ng konteksto para sa paglulunsad ng mga bagong coroutine. Ayon sa dokumentasyon ng Kotlin, 2025, bawat instance ng CoroutineScope ay naglalaman ng CoroutineContext at namamahala sa lahat ng coroutine na inilunsad dito. Kapag natapos ang scope (cancel), lahat ng coroutine na anak ay awtomatikong kinansela, na pumipigil sa pagtagas ng memorya.
Mga Pangunahing Punto
CoroutineScope — ay isang pangunahing interface mula sa library ng kotlinx.coroutines na nagsisilbing lalagyan para sa mga coroutine. Tinutukoy nito ang mga hangganan ng buhay ng mga coroutine: kapag natapos ang scope, lahat ng coroutine sa loob nito ay awtomatikong kinakansela.
public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}
Ang interface ay naglalaman lamang ng isang field — coroutineContext. Sa pamamagitan nito, ang scope ay nagbibigay ng dispatcher (Dispatcher), gawain (Job), pangangasiwa ng exception, at iba pang elemento ng konteksto para sa lahat ng coroutine na inilunsad dito.
Lahat ng function ng paglulunsad ng coroutine — launch, async, runBlocking — ay extension function sa CoroutineScope. Nangangahulugan ito na maaari lamang silang tawagin kung mayroong object ng scope. Ang ganitong disenyo ay ginagarantiyahan na ang bawat coroutine ay may malinaw na tinukoy na magulang at siklo ng buhay.
Sa Android, bawat bahagi ng arkitektura ay may sariling scope: viewModelScope para sa ViewModel, lifecycleScope para sa Activity/Fragment. Sa mga server application, ang scope ay maaaring itali sa isang HTTP request o sa isang pool ng mga koneksyon sa database.
Ang pag-unawa sa panloob na istraktura ng CoroutineScope ay nangangailangan ng pamilyaridad sa konsepto ng Job at prinsipyo ng konkurensiyang struktural.
Bawat coroutine sa paglulunsad ay nagbabalik ng Job object (o Deferred para sa async). Job ay kumakatawan sa isang gawain na may tinukoy na siklo ng buhay: New, Active, Completing, Completed, Cancelling, Cancelled. Ang mga Job object ay bumubuo ng istraktura ng puno:
Konkurensiyang struktural — pangunahing prinsipyo ng arkitektura ng Kotlin Coroutines, kung saan ang buhay ng coroutine ay nakatali sa buhay ng scope nito. Ito ay kaibahan sa modelo ng “fire-and-forget”, kung saan ang coroutine ay patuloy na nabubuhay pagkatapos ng pagtatapos ng scope. Mga bentahe ng konkurensiyang struktural:
Kapag tinawag ang scope.cancel(), ang Job ng scope ay pumunta sa status na Cancelled, na kung saan ay recursively kinakansela ang lahat ng Job na anak. Pagkatapos ng pagkansela, ang scope ay maaari lamang magamit muli sa pamamagitan ng paglikha ng bagong instance ng CoroutineScope.
Ang CoroutineScope ay maaaring likhain sa pamamagitan ng factory function o sa pamamagitan ng pagpapatupad ng interface sa iyong klase. Talakayin natin ang parehong approach.
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
scope.launch {
println("Tumatakbo sa ${Thread.currentThread().name}")
}
Ang factory function ay tumatanggap ng CoroutineContext at lumilikha ng scope na may tinukoy na konteksto. Sa halimbawa, ginamit ang Dispatchers.Default para sa mga gawaing CPU-intensive at SupervisorJob na nagbubukod ng mga exception sa pagitan ng mga coroutine na anak.
class MyRepository {
private val scope = CoroutineScope(Dispatchers.IO + Job())
suspend fun fetchData(): Data = scope.async {
api.getData()
}.await()
fun cleanup() {
scope.cancel()
}
}
Itinatago namin ang scope bilang field ng klase at manu-manong tinatawagan ang cleanup para kanselahin ito. Ito ay angkop para sa mga bahagi na may pinamamahalaang siklo ng buhay — halimbawa, para sa mga repositoryo o manager.
Pinapayagan ng Kotlin ang delegasyon ng pagpapatupad ng CoroutineScope sa pamamagitan ng keyword na by:
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
fun load() {
launch {
// coroutine ay tumatakbo sa DataLoader scope
}
}
}
Ang approach na ito ay maginhawa kapag ang klase mismo ay isang scope at nais na magbigay ng mga pamamaraan ng paglulunsad ng coroutine. Gayunpaman, mag-ingat: minamana ng klase ang lahat ng pamamaraan ng CoroutineScope, kabilang ang cancel, na maaaring lumabag sa encapsulation.
GlobalScope — ay isang singleton ng CoroutineScope para sa buong application. Ang paggamit nito sa production code ay opisyal na hindi inirerekomenda.
Pinapahintulutan ng JetBrains ang GlobalScope lamang sa mga bihirang sitwasyon: mga proseso sa background sa antas ng application na dapat manatiling buhay kahit na matapos isara ang lahat ng Activity (halimbawa, pag-sync ng data, analytics). Ngunit kahit sa mga kasong ito, mas mainam na lumikha ng sariling scope gamit ang CoroutineScope(SupervisorJob()).
Palaging gumamit ng pasadyang CoroutineScope na may tahasang pamamahala ng siklo ng buhay. Sa Android, ito ay viewModelScope at lifecycleScope. Sa mga server application, lumikha ng scope para sa bawat request o pool ng mga koneksyon.
Parehong function ay suspend function na lumilikha ng pansamantalang scope para sa mga parallel na gawain, ngunit ang kanilang pag-uugali sa mga exception ay pangunahing naiiba.
| Katangian | coroutineScope | supervisorScope |
|---|---|---|
| Pag-uugali sa error | Exception sa coroutine na anak ay kinakansela ang lahat ng iba pa | Exception sa coroutine na anak ay HINDI kinakansela ang iba |
| Pagkalat ng error | Oo, ang unang exception ay kumakalat sa labas | Oo, ang unang exception ay kumakalat sa labas |
| Default na Job | Job() — mga anak ay nakatali sa magulang | SupervisorJob() — mga anak ay hindi nakadepende sa isa’t isa |
| Karaniwang use-case | Atomikong operasyon mula sa maraming hakbang | Independiyenteng parallel na gawain (mga UI load) |
Gamitin ang coroutineScope kapag maraming parallel na operasyon ang bumubuo ng isang atomikong operasyon. Halimbawa, pagkarga ng data mula sa tatlong server: kung ang isang request ay bumagsak, ang iba ay walang saysay.
suspend fun loadProductPage(): ProductPage = coroutineScope {
val product = async { api.getProduct() }
val reviews = async { api.getReviews() }
ProductPage(product.await(), reviews.await())
}
Kung ang getProduct o getReviews ay naghagis ng exception — parehong coroutine ay kinansela, at ang exception ay kumakalat sa tumatawag na code.
Gamitin ang supervisorScope kapag ang mga parallel na operasyon ay hindi nakadepende sa isa’t isa. Halimbawa, pagkarga ng data ng profile sa maraming independiyenteng seksyon: kung ang seksyon ng mga rekomendasyon ay bumagsak, ang header ng profile at listahan ng mga kaibigan ay dapat ipakita.
Talakayin natin ang pinakakaraniwang pagkakamali ng mga developer sa paggamit ng CoroutineScope sa Kotlin.
Ang pinakakaraniwang sitwasyon ng pagtagas ng coroutine — paglikha ng scope nang hindi tinatawagan ang cancel kapag natapos ang bahagi. Kung ang scope ay hindi nakansela, ang mga coroutine ay patuloy na tumatakbo, na may hawak na mga reference sa mga bagay. Sa Android, gamitin ang viewModelScope o lifecycleScope, na awtomatikong kinakansela.
Binabalewala ng GlobalScope ang siklo ng buhay ng mga bahagi ng Android. Ang coroutine na inilunsad sa GlobalScope pagkatapos isara ang Activity ay magpapatuloy sa pagtakbo at susubukan na i-update ang UI — na magdudulot ng crash. Palaging gamitin ang lifecycleScope para sa mga bahagi ng UI.
Pagkatapos tawagan ang cancel(), ang scope ay hindi maaaring magamit muli — lahat ng coroutine sa loob nito ay tapos na. Lumikha ng bagong instance ng CoroutineScope sa pamamagitan ng factory function. Job() ay hindi sumusuporta sa muling pag-activate.
Sa delegasyon sa pamamagitan ng by, ang klase ay nakakakuha ng pampublikong pamamaraan na cancel() na maaaring tawagan mula sa kahit saan, lumalabag sa encapsulation. Itago ang scope bilang pribadong field, huwag i-delegate ang interface.
Mga Madalas Itanong
CoroutineScope — ay isang interface na nagmamay-ari ng CoroutineContext at responsable para sa siklo ng buhay ng mga coroutine. CoroutineContext — ay isang set ng mga elemento (dispatcher, job, pangangasiwa ng error) na tumutukoy “paano” isinasagawa ang coroutine. Isa sa mga pagkakaiba: scope lumilikha ng coroutine, context namamahala ng kanilang pag-uugali.
Oo, ito ay isang karaniwang pattern: CoroutineScope(Dispatchers.IO + SupervisorJob()). Pinipigilan ng SupervisorJob ang cascading cancellation ng mga coroutine na anak kapag may exception sa isa sa kanila. Ito ay kapaki-pakinabang para sa mga independiyenteng parallel na gawain kung saan ang error sa isa ay hindi dapat huminto sa iba.
Walang mga limitasyon sa bilang ng mga coroutine sa isang scope — sila ay limitado lamang ng magagamit na memorya at mga setting ng dispatcher. Ang praktikal na limitasyon ay karaniwang libu-libong aktibong coroutine sa isang scope. Gayunpaman, ang malaking bilang ng coroutine ay maaaring magpahiwatig ng mga problema sa arkitektura.
Ang tamang paraan ay ipasa ang scope sa klase sa pamamagitan ng constructor o gamitin ang runBlockingTest / runTest mula sa kotlinx-coroutines-test. Sa mga pagsubok, maaari mong palitan ang scope ng TestCoroutineDispatcher at manu-manong kontrolin ang pagpapatupad ng coroutine.
Hindi, ang scope ay isang panlabas na lalagyan para sa coroutine. Ang coroutine mismo ay hindi isang scope. Gayunpaman, sa loob ng coroutine, maaaring lumikha ng bagong scope sa pamamagitan ng coroutineScope o supervisorScope para sa parallel na paglulunsad ng mga coroutine na anak.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din