Tamang paggamit ng mga Dispatcher: Main, IO at Default

  • Tinutukoy ng mga dispatcher ang thread ng pagpapatupad, na ino-optimize ang mga gawain ayon sa kung ang mga ito ay CPU, I/O o user interface.
  • Ang paggamit ng withContext ay nagbibigay-daan sa iyong ligtas na baguhin ang konteksto ng pagpapatupad nang hindi hinaharangan ang pangunahing thread.
  • Tinitiyak ng structured concurrency gamit ang Scopes and Jobs na ang mga gawain ay nakansela nang tama, na pumipigil sa mga memory leak.

Wastong paggamit ng Main, IO at Default Dispatchers

Kung naramdaman mo na ang iyong Application ng Android Kung mag-freeze ito habang naglo-load ng data, malamang na nahihirapan ka sa pangunahing thread. Ang mga Kotlin coroutine ay naging malaking tulong para sa maraming developer, na nagpapahintulot sa amin na magsulat ng asynchronous code na Lumilitaw itong sunod-sunod at linyar, inaalis ang mga walang katapusang callback na nagmukhang hagdanan ang code.

Para masulit ang superpower na ito, hindi sapat ang basta pagta-type lang ng salita suspend bago ang isang pagtatanghal. Ang talagang mahalaga ay ang pag-alam kung saan ito isinasagawa bawat piraso ng code, at diyan pumapasok ang mga Dispatcher, na kumikilos bilang mga konduktor ng isang orkestra na nagpapasya kung aling thread ang namamahala sa bawat gawain.

Ano ba talaga ang isang Dispatcher?

Sa madaling salita, ang isang Dispatcher ay responsable sa pagtatalaga ng isang coroutine sa isang sinulid o grupo ng mga sinulid Mga Detalye. Dapat nating tandaan na, bagama't magaan at hindi nakaharang ang mga coroutine, mayroon pa ring mga thread na tumatakbo sa background na sumusuporta sa mga ito. Ang pag-configure ng naaangkop na dispatcher ay pumipigil sa pag-crash ng app o sa pagbaba ng performance.

Kapag naglulunsad tayo ng coroutine gamit ang launchKaraniwan itong nagmamana ng konteksto kung saan ito nilikha. Gayunpaman, kung kailangan nating ilipat ang isang partikular na bahagi ng lohika sa ibang thread, maaari natin itong tahasang ipahiwatig. Upang masuri kung saang thread talaga tayo naroroon, isang napaka-kapaki-pakinabang na paraan ay ang pag-print Thread.currentThread().name, na nagbibigay-daan sa amin upang makita ang mahika na nangyayari sa totoong oras.

Ang dynamic trio: Main, IO, at Default

Nag-aalok ang Kotlin ng ilang opsyon, ngunit may tatlo na gagamitin mo sa 99% ng oras. Una, mayroon tayo Mga Dispatcher.MainIto ang UI thread. Dapat lamang itong gamitin para sa napakabilis na mga gawain, tulad ng pag-update ng teksto sa screen o pakikipag-ugnayan sa mga bahagi ng UI. Kung magsisimula kang magsagawa ng mga kalkulasyon dito, mag-crash ang app at madidismaya ang user.

Pagkatapos mayroon tayo Mga Dispatcher.IOna siyang pinakamabisang paraan para sa lahat ng input at output ng data. Ito ay mainam para sa magbasa ng mga file, gumawa ng mga kahilingan sa isang API o mag-query sa database gamit ang Room. Gumagamit ang dispatcher na ito ng mas malaking thread pool dahil ang mga I/O task ay kadalasang gumugugol ng maraming oras sa paghihintay para sa isang panlabas na tugon nang hindi kumukonsumo ng CPU.

Sa wakas, nakita natin Mga Dispatcher.DefaultIto ay na-optimize para sa mga gawaing nangangailangan ng maraming processor, ibig sabihin, Trabahong nakatali sa CPUKung kailangan mong i-parse ang isang napakalaking JSON file, ayusin ang isang malaking listahan, o magsagawa ng mga kumplikadong kalkulasyon sa matematika, ito ang lugar para sa iyo. Ang mga thread nito ay karaniwang itinutugma sa bilang ng mga CPU core upang maiwasan ang labis na pagkarga sa sistema.

Mayroon ding Dispatchers.Unconfined, ngunit ang totoo iyan mahirap gamitin Sa mga proyekto sa totoong mundo, nagsisimula ito saanman ito tinawag at maaaring ipagpatuloy sa anumang thread, kaya naman sa karamihan ng mga kaso ay medyo hindi mahuhulaan ang proseso.

Ang sining ng pagpapalit ng mga thread gamit ang withContext

Dito nangyayari ang mahika seguridad ng pangunahing thread (pangunahing kaligtasan). Ang tungkulin withContext Pinapayagan ka nitong suspindihin ang kasalukuyang coroutine, lumipat sa ibang dispatcher, isagawa ang code block, at, kapag tapos na, awtomatikong bumalik sa orihinal na dispatcher.

Ang pinakamagandang bagay tungkol sa pattern na ito ay hindi kailangang mag-alala ang nagpadala ng function kung saang thread ito tatawagan. Isang magandang kasanayan ang pagkakaroon ng suspendidong function mismo... pamahalaan ang sarili mong kontekstoHalimbawa, isang tungkulin getDatos() dapat tumawag sa loob withContext(Dispatchers.IO)Sa ganitong paraan, matatawag ito ng ViewModel mula sa pangunahing thread nang walang takot na harangan ang screen.

Sa mga tuntunin ng pagganap, withContext Ito ay lubos na mahusay. Hindi ito nagdaragdag ng anumang karagdagang karga kumpara sa mga callback, at kung minsan ay nagagawa ng Kotlin na i-optimize ang pagpapalit ng thread Kung matutukoy nito na nasa nais na dispatcher na tayo, maiiwasan nito ang mga hindi kinakailangang pagbabago.

Pagkontrol sa siklo ng buhay: Mga Saklaw at Trabaho

Tamang paggamit ng mga Dispatcher: Main, IO at Default

Ang random na paglulunsad ng mga coroutine ay isang paraan para sa kapahamakan at mga tagas sa memorya. Para maiwasan ito, ginagamit namin ang CoroutineScopesna siyang tumutukoy sa tagal ng gawain. Sa Android, mayroon tayo viewModelScope para sa mga ViewModel at lifecycleScope Para sa mga Aktibidad o mga Fragment; kapag namatay ang component, awtomatikong kakanselahin ang mga coroutine.

Sa tuwing ginagamit natin launch o async, a ay nabuo Bagay sa trabahoAng Trabahong ito ay parang isang tiket na nagbibigay-daan sa atin na kontrolin ang coroutine: magagamit natin job.join() hintayin itong matapos o job.cancel() para itigil ito kung ang resulta ay hindi na mahalaga sa atin. Ito ang batayan ng nakabalangkas na sabay-sabay na pagkakasunod-sunod: ang mga bata ay nakaugnay sa ama at walang mga prosesong naulila na natitira sa alaala.

Parallel execution at ang pagkakaiba sa pagitan ng launch at async

Minsan gusto nating gawin ang ilang bagay nang sabay-sabay para makatipid ng oras. Para dito ginagamit natin async, na nagbabalik ng isang bagay Deferred. Hindi katulad launch (na karaniwang isang Abutin at kalimutan.), async Pinapayagan tayo nitong makuha ang isang halaga gamit ang function await().

Bigyang-pansin dito: na iyong ginagamit async Hindi ito awtomatikong nangangahulugan na mayroong paralelismo. Kung maglulunsad ka ng dalawa async en Dispatchers.MainIsa-isa silang isasagawa sa iisang thread. Upang makamit ang isang tunay na paralelismoDapat mo silang italaga Dispatchers.Default o IOna nagpapahintulot sa mga ito na maipamahagi sa iba't ibang mga core ng CPU.

Para pamahalaan ang mga grupo ng magkakatulad na gawain, ang tagabuo coroutineScope Ito ay mahalaga. Tinitiyak nito na ang tungkulin ay hindi matatapos hangga't hindi natatapos ng lahat ng batang coroutine ang kanilang trabaho, at sinusuri rin ang anumang mga eksepsiyon na maaaring lumitaw sa proseso upang hindi sila maligaw sa kawalan.

Para maisara ang loop, mahalagang huwag labis na gamitin GlobalScope dahil napakahirap subukan at kanselahin. Sa isip, dapat laging umasa ang isa sa Hierarchy ng saklaw ng Android at gamitin ang tamang dispatcher ayon sa workload. Sa pamamagitan ng pagsasama-sama viewModelScope, withContext para sa mabibigat na gawain at async Para sa sabay-sabay na paggamit, nakakamit namin ang matatag na mga aplikasyon na nagpapanatili ng maayos na interface habang pinoproseso ang data sa background nang hindi nagpapakahirap.


Idagdag bilang ginustong mapagkukunan