Как да замените AsyncTask с Coroutines на Kotlin

Все още ли използвате AsyncTask във вашите приложения за Android? Вероятно вече не трябва да си. Ето как да ги замените с Coroutines на Kotlin.

За много дълго време в Android, ако трябваше да правите нещо асинхронно, когато правите приложение, вероятно ще използвате AsyncTask. AsyncTask е API в рамката на Android, което улеснява (ish) изпълнението на операции във фонов режим и връщането на стойности, когато приключи. И това има смисъл. За разлика от Coroutines на Kotlin, AsyncTask съществува от известно време и е вграден направо.

Въпреки това, както философията на дизайна, така и внедряването на AsyncTask са остарели донякъде през годините. Поради това Google има отхвърли AsyncTask API. Все още можете да го използвате, ако искате, но Google не препоръчва да го правите. За щастие има цял куп алтернативи на AsyncTask, включително характеристика на езика Kotlin - съпрограмми.

API на съпрограммите на Kotlin е невероятно мощна рамка, която ви позволява да правите цял куп неща. Тази статия само ще надраска повърхността на това, което е възможно. Ще разгледаме основите, необходими за мигриране от AsyncTask към съпрограми.

Добавяне на поддръжка на Coroutines

Преди да започнете да използвате съпрограмми, трябва действително да ги добавите към вашия проект.

Добавяне на поддръжка на Kotlin

Ако вече сте внедрили Kotlin, преминете към следващия раздел. В противен случай ще трябва да добавите поддръжка на Kotlin към вашия проект. Вижте моя урок за добавяне на Kotlin към съществуващ проект за повече подробности.

Добавяне на Coroutine библиотеки

На вашето модулно ниво build.gradle, включват следните зависимости.

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

Синхронизирайте проекта си и съпрограммите на Kotlin вече ще бъдат достъпни за използване.

Използване на Coroutines

Внедряване на CoroutineScope

За да използвате съпрограми, ще трябва да имате наличен екземпляр на CoroutineScope. Лесен начин да направите това е просто да го внедрите във вашия съдържащ клас.

Например, за да внедрите CoroutineScope в дейност:

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

override fun onDestroy(){
super.onDestroy()

cancel()
}
}

Това ще накара SomeActivity да внедри интерфейса CoroutineScope чрез класа MainScope. MainScope ще обработва цялата логика на внедряване за CoroutineScope, като същевременно ви позволява да използвате методите на CoroutineScope. Обаждане cancel() в onDestroy() гарантира, че никаква асинхронна логика не продължава да работи след излизане на дейността.

Замяна на AsyncTask с Coroutines

Да кажем, че имате AsyncTask в дейност, която изпълнява продължителна операция във фонов режим и в крайна сметка връща низ. Нещо като следното.

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
}
}

Замяната на това със съпрограма е лесна. Просто използвайте async() метод. Котлин async() работи на която и нишка да е стартирано, но го прави асинхронно. Това означава, че можете да актуализирате Views и подобни, без да се налага да се притеснявате за използването на правилната нишка.

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"
}
}
}

Както можете да видите, използването на съпрограми може да бъде много по-лесно от използването на AsyncTask. Не е нужно просто да се обаждате async() и го остави да си свърши работата. Можете да задържите препратка към него и дори да изчакате да приключи.

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()

Връщане на стойности с async

Можете дори да върнете стойност от async() ако искаш. Така че оригиналният пример може да стане нещо подобно.

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
}
}

Използване на withContext

За удобство Kotlin предоставя withContext(). Това вгражда цялото await() нещо и просто ви връща стойността.

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
}
}

Заключение

Примерите по-горе са само някои основни начини на използване на съпрограммите на Kotlin, за да започнете. Не е нужно да ограничавате съпрограммите до дейности или дори нещо с подходящ жизнен цикъл. Можете да ги стартирате основно навсякъде. Има и по-усъвършенствани операции, като избор на коя нишка да изпълнява асинхронната логика. Това ръководство е основно за показване как да замените проста AsyncTask с проста съпрограма.

За повече подробности как работят съпрограмите и как можете да използвате техните по-разширени функции, вижте официална документация на Kotlin.