
1. Overview
In this article, we will learn the alternative solutions to the deprecated AsyncTask in Android using Kotlin.
An AsyncTask is an asynchronous task that runs on a background thread and publishes the result on the UI thread.
This API is deprecated in API level 30 and recommended using
java.util.concurrent
- Kotlin concurrency utilities.
Here, we will use the java.util.concurrent
.
2. Why AsyncTask is deprecated in Android
AsyncTask could cause
- Context leaks
- Missed callbacks
- Crashes on configuration changes
- Inconsistent behavior on different versions of the platform
- Swallows exceptions from
doInBackground
. Error hiding or Swallowing exception is the practice of catching an error or exception, and then continuing without logging, processing, or reporting the error to other parts of the app. Swallowing exceptions are considered bad practice as information about the error is lost and makes it very hard to track down the root cause of the problems. - Provide little utility when compared with
Executor
s directly.
AsyncTask is a helper class around Thread
and Handler
and does not have any components that can manage threads efficiently.
You can use the various APIs provided by the java.util.concurrent
package such as Executor
, ThreadPoolExecutor
and FutureTask
.
3. AsyncTask deprecated alternative Android - Kotlin
Here, we are using the Executor
class as an alternative to the deprecated AsyncTask.
First, create an instance of the executor by using any of the factory methods:
private val executor: Executor = Executors.newSingleThreadExecutor()
A Handler gives you a mechanism to push tasks into the UI thread queue from any other threads thus allowing other threads to communicate with the UI thread. Looper
includes a helper function, getMainLooper()
, which retrieves the Looper
of the main thread. To learn more about Handler, see this article.
Now, let’s retrieve the handler of the UI thread to publish error or results back from the asynchronous task.
private val handler = Handler(Looper.getMainLooper())
You can use execute method of the Executor
class to execute any task asynchronously and publish error or result back to the UI thread by using the handler.
executor.execute { val result: R try { // perform task asynchronously // you can also execute runnable or callable handler.post { // update the result to the UI thread // or any operation you want to perform on UI thread. It is similar to onPostExecute() of AsyncTask } } catch (e: Exception) { e.printStackTrace() handler.post { // update error to UI thread or handle } } }
You can use the above code in your app to run tasks asynchronously replacing the AsyncTask
. However, you can also use the below class that contains the above Executor
logic in a reusable way that would make the code cleaner:
package com.tedblob.rxjava import android.os.Handler import android.os.Looper import java.util.concurrent.Callable import java.util.concurrent.Executor import java.util.concurrent.Executors class ExecutorRunner { // create a new instance of Executor using any factory methods private val executor: Executor = Executors.newSingleThreadExecutor() // handler of UI thread private val handler = Handler(Looper.getMainLooper()) // callable to communicate the result back to UI interface Callback<R> { fun onComplete(result: R) fun onError(e: Exception?) } fun <R> execute(callable: Callable<R>, callback: Callback<R>) { executor.execute { val result: R try { // execute the callable or any tasks asynchronously result = callable.call() handler.post { // update the result back to UI callback.onComplete(result) } } catch (e: Exception) { e.printStackTrace() handler.post { // communicate error or handle callback.onError(e) } } } } }
You can also execute Runnable
or Callable
using the Executor
. We will execute the below Callable
task by using the execute
method of the Executor
interface.
class SampleCallable(private val input: String) : Callable<String> { override fun call(): String { return input } }
You can create an instance of the above ExecutorRunner
and pass the above callable task to run asynchronously.
executorRunner?.execute( SampleCallable("Input for Sample Callable"), object : ExecutorRunner.Callback<String> { override fun onComplete(result: String) { // handle the result obtained from the asynchronous task } override fun onError(e: Exception?) { // handle the result obtained from the asynchronous task } })
4. Conclusion
To sum up, we have learned the alternative to deprecated AsyncTask in Android (Kotlin).