
1. Overview
In this article, we will learn the alternative solutions to the deprecated AsyncTask in Android using Java.
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 - Java
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 final 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 final Handler handler = new 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(() -> { final R result; 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 (Exception e) { 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; public class ExecutorRunner { // Executor instance private final Executor executor = Executors.newSingleThreadExecutor(); // UI thread handler private final Handler handler = new Handler(Looper.getMainLooper()); // callback interface to communicate result or error public interface Callback<R> { void onComplete(R result); void onError(Exception e); } public <R> void execute(Callable<R> callable, Callback<R> callback) { // Executor's execute method to execute the task asynchronously executor.execute(() -> { final R result; try { result = callable.call(); handler.post(() -> { callback.onComplete(result); }); } catch (Exception e) { e.printStackTrace(); handler.post(() -> { 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.
package com.tedblob.rxjava; import java.util.concurrent.Callable; class SampleCallable implements Callable<String> { private final String input; public SampleCallable(String input) { this.input = input; } @Override public String call() { return input; } }
You can create an instance of the above ExecutorRunner
and pass the above callable task to run asynchronously.
executorRunner.execute(new SampleCallable("Input for Sample Callable"), new ExecutorRunner.Callback<String>() { @Override public void onComplete(String result) { // Result returned from the asynchronous task } @Override public void onError(Exception e) { // any error while running the task asynchronously } });
4. Conclusion
To sum up, we have learned the alternative to deprecated AsyncTask in Android (Java).