Ako ste ikada osjetili da vaš Android aplikacija Ako se zamrzne prilikom učitavanja podataka, vrlo je vjerojatno da imate problema s glavnom niti. Kotlin korutine su spasile mnoge programere, omogućujući nam pisanje asinkronog koda koji Čini se sekvencijalnim i linearnim, eliminirajući one beskonačne povratne pozive koji su kod činili da izgleda kao ljestve.
Da biste iskoristili ovu supermoć, nije dovoljno samo upisati riječ suspend prije nastupa. Ono što je zaista važno je znati gdje se izvršava svaki dio koda, i tu dolaze Dispečeri, koji djeluju kao dirigenti orkestra i odlučuju koja je nit zadužena za svaki zadatak.
Što je, dovraga, Dispečer?
U osnovi, Dispečer je odgovoran za dodjeljivanje korutine nit ili skupina niti Specifičnosti. Moramo imati na umu da, iako su korutine lagane i ne blokiraju, još uvijek postoje niti koje se izvode u pozadini i koje ih podržavaju. Konfiguriranje odgovarajućeg dispečera sprječava rušenje aplikacije ili pad performansi.
Kada pokrenemo korutinu s launchObično nasljeđuje kontekst u kojem je stvoren. Međutim, ako nam je potrebno da se određeni dio logike premjesti u drugu nit, to možemo eksplicitno naznačiti. Da bismo provjerili u kojoj se niti zapravo nalazimo, vrlo koristan trik je ispisati Nit.trenutnaNit().naziv, što nam omogućuje da vidimo magiju koja se događa u stvarnom vremenu.
Dinamički trio: Glavni, IO i Zadani
Kotlin nam nudi nekoliko opcija, ali postoje tri koje ćete koristiti 99% vremena. Prvo, imamo Dispečeri.GlavniOvo je nit korisničkog sučelja. Treba je koristiti samo za vrlo brze zadatke, poput ažuriranja teksta na zaslonu ili interakcije s komponentama korisničkog sučelja. Ako ovdje počnete izvoditi izračune, aplikacija će se srušiti i korisnik će postati frustriran.
Onda imamo Dispečeri.IOkoji je glavni pokretač za sav unos i izlaz podataka. Idealan je za čitati datoteke, slati zahtjeve API-ju ili upitajte bazu podataka s Room. Ovaj dispečer koristi puno veći skup niti jer I/O zadaci često provode puno vremena čekajući vanjski odgovor bez trošenja CPU-a.
Napokon, nalazimo Dispečeri.ZadanoOvo je optimizirano za zadatke koji intenzivno koriste procesor, tj. Rad vezan uz procesorAko trebate parsirati ogromnu JSON datoteku, sortirati ogroman popis ili izvršiti složene matematičke izračune, ovo je mjesto za vas. Njegove niti su obično usklađene s brojem jezgri procesora kako bi se izbjeglo preopterećenje sustava.
Tu je također Dispatchers.Unconfined, ali istina je takva jedva korišten U stvarnim projektima, počinje gdje god se pozove i može se nastaviti u bilo kojoj niti, što ga u većini slučajeva čini prilično nepredvidljivim.
Umjetnost prebacivanja niti s withContextom
Ovdje se događa magija sigurnost glavne niti (glavna sigurnost). Funkcija withContext Omogućuje vam obustavu trenutne korutine, prelazak na drugi dispečer, izvršavanje bloka koda i, nakon završetka, automatski povratak na izvorni dispečer.
Najbolja stvar kod ovog obrasca je što pošiljatelj funkcije ne mora brinuti o tome u kojoj niti će je pozvati. Dobra praksa je da se sama suspendirana funkcija... upravljajte vlastitim kontekstomNa primjer, funkcija getDatos() trebao bi interno nazvati withContext(Dispatchers.IO)Na ovaj način, ViewModel ga može pozvati iz glavne niti bez straha od blokiranja ekrana.
Što se tiče izvedbe, withContext Iznimno je učinkovit. Ne dodaje nikakvo dodatno opterećenje u usporedbi s povratnim pozivima, a Kotlin ponekad može optimizirati zamjenu niti Ako otkrije da smo već u željenom dispečeru, izbjegava nepotrebne skokove.
Kontroliranje životnog ciklusa: Opsezi i poslovi

Nasumično pokretanje korutina recept je za katastrofu i curenje memorije. Kako bismo to izbjegli, koristimo CoroutineScopeskoji definiraju životni vijek zadatka. U Androidu imamo viewModelScope za ViewModele i lifecycleScope Za aktivnosti ili fragmente; kada komponenta prestane raditi, korutine se automatski otkazuju.
Svaki put kada koristimo launch o async, generira se a Objekt poslaOvaj posao je poput karte koja nam omogućuje kontrolu korutine: možemo koristiti job.join() čekati da završi ili job.cancel() zaustaviti ga ako nas ishod više ne zanima. To je osnova strukturirana konkurentnostDjeca su povezana s ocem i ne postoje osiroćeni procesi koji zaostaju u sjećanju.
Paralelno izvršavanje i razlika između pokretanja i asinkronosti
Ponekad želimo učiniti nekoliko stvari odjednom kako bismo uštedjeli vrijeme. Za to koristimo async, koji vraća objekt Deferred. Za razliku od launch (što je u osnovi Pucaj i zaboravi.), async Omogućuje nam da dohvatimo vrijednost pomoću funkcije await().
Obratite pozornost ovdje: da koristite async To ne znači automatski da postoji paralelizam. Ako pokrenete dva async en Dispatchers.MainIzvršavat će se jedan za drugim u istoj niti. Da bi se postigao pravi paralelizamMorate ih dodijeliti Dispatchers.Default o IOšto im omogućuje distribuciju po različitim jezgrama CPU-a.
Za upravljanje grupama paralelnih zadataka, alat za izradu coroutineScope Ovo je osnovno. Osigurava da funkcija ne završava dok sve podređene korutine ne završe svoj posao, a također hvata sve iznimke koje se mogu pojaviti putem kako se ne bi izgubile u limbu.
Za zatvaranje kruga, bitno je ne pretjerivati GlobalScope budući da je vrlo teško testirati i otkazati. Idealno bi bilo uvijek se osloniti na Hijerarhija opsega Androida i koristite ispravnog dispečera prema opterećenju. Kombiniranjem viewModelScope, withContext za teške zadatke i async Za konkurentnost postižemo robusne aplikacije koje održavaju fluidno sučelje dok obrađuju podatke u pozadini bez ikakvog napora.