Alternatives for the Deprecated AsyncTask in Android
AsyncTask (Asynchronous Task) in Android is an abstract class, or rather a helper class that lets the application perform tedious tasks in the background and parallelly perform UI changes in the front-end. This is quite similar to Threading but does not constitute any threading framework. Background tasks such as loading images, downloading music, pictures, files, etc can be performed using AsyncTask. However, AsyncTask is now deprecated and developers might sooner or later need an alternative for this.
Through this article, we will show you 2 methods through which you can perform background tasks and simultaneously update the UI elements. The two alternatives are:
- Using a combination of an Executor and a Handler
- Using a Thread
But first, let us look at how AsyncTask works.
AsyncTask General Code Structure
The below code implements an AsyncTask to an inner class MyCustomClass. AsyncTask requires three arguments:
- Params: Can be of type Int, String, any object given as Input for executing the supposed task, Void when there is no input.
- Progress: Units published while executing the task, Void when not needed.
- Result: Can be of type Int, String, any object when the task is finished. Void when no output variable is needed.
Associated member functions are:
- onPreExecute() Optional
- doInBackground(Params…) Required
- onProgressUpdate(Progress…) Optional
- onPostExecute(Result) Optional
Below is the basic code structure for implementing AsyncTask.
Kotlin
import android.os.AsyncTask import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) } // AsyncTask <Params: Any!, Progress: Any!, Result: Any!>() inner class MyCustomClass: AsyncTask<String, Void, String>(){ override fun doInBackground(vararg params: String?): String { TODO() } override fun onPostExecute(result: String?) { TODO() } } } |
Example:
Now in this example, we shall pass an Integer value (Input) to the Task. In the background, the input is converted into a String, and after the conversion, it will be displayed in a TextView.
Kotlin
import android.annotation.SuppressLint import android.os.AsyncTask import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.textView) val myInput = 100 MyCustomClass(myTextView).execute(myInput) } // Input type is Int and Result is a String @SuppressLint ( "StaticFieldLeak" ) inner class MyCustomClass(var textView: TextView): AsyncTask<Int, Void, String>(){ override fun doInBackground(vararg params: Int?): String { // Convert the input Params:Int // to String and return the Result:String return params[ 0 ].toString() } // Result:String is set as text in the passed TextView override fun onPostExecute(result: String?) { textView.text = result } } } |
XML
<? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:app = "http://schemas.android.com/apk/res-auto" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > < TextView android:id = "@+id/textView" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_centerInParent = "true" /> </ RelativeLayout > |
Output:
So basically, the code works perfectly fine. However, we have used AsyncTask to perform our task, which is deprecated and sooner or later requires a replacement.
Alternative 1: Using Executor and Handler
There is a sample code below. If you see, there is an executor and a handler declared. The executor will help in performing any task in the background and the handler will help to make UI changes.
Kotlin
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Handler import android.os.Looper import java.util.concurrent.Executors class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myExecutor = Executors.newSingleThreadExecutor() val myHandler = Handler(Looper.getMainLooper()) myExecutor.execute { // Do something in background (back-end process) } myHandler.post { // Do something in UI (front-end process) } } } |
Example:
We used the same example as we used for AsyncTask. Here the executor will execute the task and as soon as the desired results are available, UI changes are made using a handler.
Kotlin
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Handler import android.os.Looper import android.widget.TextView import java.util.concurrent.Executors class MainActivity : AppCompatActivity() { private val myExecutor = Executors.newSingleThreadExecutor() private val myHandler = Handler(Looper.getMainLooper()) override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.textView) val myInput = 55 doMyTask(myTextView, myInput) } private fun doMyTask(textView: TextView, input: Int){ myExecutor.execute { val result = input.toString() myHandler.post { textView.text = result } } } } |
XML
<? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:app = "http://schemas.android.com/apk/res-auto" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > < TextView android:id = "@+id/textView" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_centerInParent = "true" /> </ RelativeLayout > |
Output:
The code runs perfectly fine. We have got the desired results.
Alternative 2: Using a Thread
The below code runs a Thread. The marked comments show where the codes for background and UI changes are to be made. When using a Thread, any UI changes must run inside a UI Thread, and any background process must run outside the UI Thread.
Kotlin
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) Thread(Runnable { // Do something in Background (Back-end) runOnUiThread { // Do something in UI (Front-end) } }).start() } } |
Example:
Now let us look at an example where we shall pass an Integer for converting it into a String and pass it to the TextView.
Kotlin
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.textView) val myInput = 26 doMyTask(myTextView, myInput) } private fun doMyTask(textView: TextView, input: Int){ Thread(Runnable { val result = input.toString() runOnUiThread { textView.text = result } }).start() } } |
XML
<? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:app = "http://schemas.android.com/apk/res-auto" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > < TextView android:id = "@+id/textView" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_centerInParent = "true" /> </ RelativeLayout > |
Output:
The code runs perfectly fine. We have our desired results.