Як замінити AsyncTask на Coroutines Kotlin

Ви все ще використовуєте AsyncTask у своїх програмах для Android? Вам, напевно, більше не повинно бути. Ось як замінити їх на Coroutines Kotlin.

Протягом дуже тривалого часу в Android, якщо вам потрібно було робити щось асинхронно під час створення програми, ви, ймовірно, використовували AsyncTask. AsyncTask — це API у фреймворку Android, який дозволяє легко виконувати операції у фоновому режимі та повертати значення після завершення. І це має сенс. На відміну від Coroutines Kotlin, AsyncTask існує вже деякий час, і його вбудовано.

Проте як філософія дизайну, так і реалізація AsyncTask за ці роки дещо застаріли. Через це Google має застарілий API AsyncTask. Ви все ще можете використовувати його, якщо хочете, але 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 тепер будуть доступні для використання.

Використання співпрограм

Реалізація CoroutineScope

Щоб використовувати співпрограми, вам потрібно буде мати доступний екземпляр CoroutineScope. Простий спосіб зробити це — просто реалізувати його у вашому класі-контейнери.

Наприклад, щоб реалізувати CoroutineScope в Activity:

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

override fun onDestroy(){
super.onDestroy()

cancel()
}
}

Це змусить SomeActivity реалізувати інтерфейс CoroutineScope за допомогою класу MainScope. MainScope оброблятиме всю логіку реалізації для CoroutineScope, дозволяючи вам використовувати методи CoroutineScope. Дзвінок cancel() в onDestroy() гарантує, що жодна асинхронна логіка не продовжує працювати після завершення дії.

Заміна AsyncTask на Coroutines

Скажімо, у вас є AsyncTask всередині Activity, яка виконує тривалу операцію у фоновому режимі та зрештою повертає рядок. Щось на зразок наступного.

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 тощо, не турбуючись про те, щоб використовувати правильний Thread.

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.