Sådan erstattes AsyncTask med Kotlins Coroutines

Bruger du stadig AsyncTask i dine Android-apps? Det burde du nok ikke være mere. Sådan erstatter du dem med Kotlins Coroutines.

I meget lang tid i Android, hvis du skulle gøre noget asynkront, når du lavede en app, ville du sandsynligvis bruge AsyncTask. AsyncTask er en API i Androids framework, der gør det nemt (ish) at køre operationer i baggrunden og returnere værdier, når du er færdig. Og det giver mening. I modsætning til Kotlins Coroutines har AsyncTask eksisteret i et stykke tid, og det er indbygget.

Både designfilosofien og implementeringen af ​​AsyncTask er dog blevet noget forældet gennem årene. Derfor har Google udfasede AsyncTask API. Du kan stadig bruge det, hvis du vil, men Google anbefaler ikke at gøre det. Heldigvis er der en hel masse alternativer til AsyncTask, inklusive en funktion af Kotlin-sproget - coroutines.

Kotlins coroutines API er en utrolig kraftfuld ramme, som lader dig gøre en hel masse ting. Denne artikel kommer kun til at ridse overfladen af, hvad der er muligt. Vi vil gennemgå det grundlæggende, der er nødvendigt for at migrere fra AsyncTask til coroutines.

Tilføjelse af Coroutines Support

Før du kan begynde at bruge coroutiner, skal du faktisk tilføje dem til dit projekt.

Tilføjelse af Kotlin Support

Hvis du allerede har implementeret Kotlin, skal du springe videre til næste afsnit. Ellers skal du tilføje Kotlin-support til dit projekt. Tjek min tutorial om at tilføje Kotlin til et eksisterende projekt for flere detaljer.

Tilføjelse af Coroutine-biblioteker

På dit modulniveau build.gradle, inkludere følgende afhængigheder.

dependencies {
...
implementation 'org.jetbrains.kotlinx: kotlinx-coroutines-core: 1.5.0'
implementation 'org.jetbrains.kotlinx: kotlinx-coroutines-android: 1.5.0'
}

Synkroniser dit projekt, og Kotlins koroutiner vil nu være tilgængelige til brug.

Brug af Coroutines

Implementering af et CoroutineScope

For at bruge coroutines skal du have en CoroutineScope-instans tilgængelig. En nem måde at gøre dette på er blot at implementere det i din indeholdende klasse.

For eksempel, for at implementere et CoroutineScope i en aktivitet:

classSomeActivity : AppCompatActivity, CoroutineScope by MainScope() {
...

override fun onDestroy(){
super.onDestroy()

cancel()
}
}

Dette vil få SomeActivity til at implementere CoroutineScope-grænsefladen ved hjælp af MainScope-klassen. MainScope vil håndtere al implementeringslogik for CoroutineScope, samtidig med at du kan bruge CoroutineScope-metoderne. Ringer cancel() i onDestroy() sørger for, at ingen asynkron logik fortsætter med at køre, efter at aktiviteten afsluttes.

Udskiftning af AsyncTask med Coroutines

Lad os sige, at du har en AsyncTask inde i en aktivitet, der udfører en langvarig operation i baggrunden og til sidst returnerer en streng. Noget i stil med følgende.

private inner classSomeTask : AsyncTask() {
override fun doInBackground(vararg params: Void): String {
try {
//Pretend this is an actual operation that takes 10 seconds and not just sleeping.
Thread.sleep(10000);
} catch (e: InterruptedException) {}

return"SomeString";
}

override fun onPostExecute(result: String) {
val someTextView = findViewById(R.id.some_text_view)
someTextView.text = result
}
}

Det er nemt at erstatte dette med en coroutine. Brug blot async() metode. Kotlins async() kører på hvilken tråd den blev lanceret på, men gør det asynkront. Det betyder, at du kan opdatere visninger og sådan uden at skulle bekymre dig om at bruge den rigtige tråd.

classSomeActivity : AppCompatActivity(), CoroutineScope by MainScope() {
...

private fun doOperation(){
async {
//Inside coroutine scopes (like inside async here), delay is used instead of Thread.sleep.
delay(10000)

val someTextView = findViewById(R.id.some_text_view)
someTextView.text = "SomeString"
}
}
}

Som du kan se, kan det være meget enklere at bruge coroutines end at bruge AsyncTask. Du skal ikke bare ringe async() og lad det dog gøre sit. Du kan holde en reference til den og endda vente på, at den er færdig.

val asyncJob = async {
//Some operation
}
//Pause here until the async block is finished.
asyncJob.await()

//This won't run until asyncJob finishes, but other operations started before the job, or started from another method, can still run.
doSomethingElse()

Returnerer værdier med asynkron

Du kan endda returnere en værdi fra async() Hvis du vil. Så det originale eksempel kunne blive sådan noget.

classSomeActivity : AppCompatActivity(), CoroutineScope by MainScope() {
...
private fun doOperation(){
val asyncJob = async {
//Inside coroutine scopes (like inside async here), delay is used instead of Thread.sleep.
delay(10000)

//Whatever the type is of the last line is what async() eventually returns.
"SomeString"
}

val result = asyncJob.await()

val someTextView = findViewById(R.id.some_text_view)
someTextView.text = result
}
}

Brug withContext

For nemheds skyld tilbyder Kotlin withContext(). Dette indlejrer helheden await() ting og returnerer bare værdien til dig.

classSomeActivity : AppCompatActivity(), CoroutineScope by MainScope() {
...
private fun doOperation(){
//Run asynchronously on the main Thread.
val result = withContext(Dispatchers.Main) {
delay(10000)

"SomeResult"
}

val someTextView = findViewById(R.id.some_text_view)
someTextView.text = result
}
}

Konklusion

Eksemplerne ovenfor er blot nogle grundlæggende brug af Kotlins coroutines for at komme i gang. Du behøver ikke at begrænse koroutiner til aktiviteter eller endda noget med en ordentlig livscyklus. Du kan køre dem stort set hvor som helst. Der er også mere avancerede operationer, som at vælge hvilken tråd der skal køre den asynkrone logik. Denne guide er hovedsageligt til at vise, hvordan man erstatter en simpel AsyncTask med en simpel coroutine.

For flere detaljer om, hvordan coroutines fungerer, og hvordan du kan gøre brug af deres mere avancerede funktioner, se officiel Kotlin-dokumentation.