AsyncTask is deprecated, now what? — Part 1

Idan Damri
The Startup
4 min readMay 26, 2020

--

If you are reading this because you just heard that starting from Android 11 AsyncTask is deprecated and you are looking for some kind of solution, you might have reached the right place.
This article is for you if you haven't moved to Kotlin you need some kind of Java solution.

Before we start I have to say that Kotlin offers the use of the amazing “coroutines” for asynchronous tasks. If you want to try it out you can do it with the following link. If you haven’t started working with Kotlin or at least reading about it I would really suggest starting doing so, since Google announced it as their official language for android development(!!). Not only that, but all of their documentation is also written in Kotlin for the new components (the old components have examples and documentation on Kotlin as well as Java) and if you are still not convinced — it reduces development times in average of about 40% which means less writing code, faster go-to-market time, it’s intuitively easy to read and has a lot of key features that I personally like as a developer. But that’s enough about Kotlin, let’s get down to business.

Let’s say you had the following class that extends AsyncTask:

public class myAsyncTask extends AsyncTask<Void, Void, String> {

WeakReference<myInteface> myIntefaceWeakReference;

public myAsyncTask(myInteface listener) {
this.myIntefaceWeakReference = new WeakReference<>(listener);
}

@Override
protected void onPreExecute() {
if(myIntefaceWeakReference != null && myIntefaceWeakReference.get() != null){
myIntefaceWeakReference.get().doWorkBeforeBackground();
}
}

@Override
protected String doInBackground(Void... voids) {
String result = someNetworkFunction();
return result;
}

@Override
protected void onPostExecute(Object s) {
if(myIntefaceWeakReference != null && myIntefaceWeakReference.get() != null){
myIntefaceWeakReference.get().doWorkAfterBackground((String)s);
}
}
}
interface myInteface {
void doWorkBeforeBackground();
void doWorkAfterBackground(Object result);
}

Now let's see the transformation I am offering, I’ll show the code first and explain about it afterward:

public interface CustomCallable<R> extends Callable<R>{
void setDataAfterLoading(R result);
void setUiForLoading();
}

I’ve created a CustomCallable interface that extends Callable, I’m using the <R> for generic purposes. Note that Callable is an interface that is why my interface extends it and not implement it!

public class TaskRunner {

private final Handler handler = new Handler(Looper.getMainLooper());
private final Executor executor = Executors.newCachedThreadPool();

public <R> void executeAsync(CustomCallable<R> callable) {
try {
callable.setUiForLoading();
executor.execute(new RunnableTask<R>(handler, callable));
} catch (Exception e) {
Utils.printStackTrace(e);
}
}

public static class RunnableTask<R> implements Runnable{
private final Handler handler;
private final CustomCallable<R> callable;

public RunnableTask(Handler handler, CustomCallable<R> callable) {
this.handler = handler;
this.callable = callable;
}

@Override
public void run() {
try {
final R result = callable.call();
handler.post(new RunnableTaskForHandler(callable, result));
} catch (Exception e) {
Utils.printStackTrace(e);
}
}
}

public static class RunnableTaskForHandler<R> implements Runnable{

private CustomCallable<R> callable;
private R result;

public RunnableTaskForHandler(CustomCallable<R> callable, R result) {
this.callable = callable;
this.result = result;
}

@Override
public void run() {
callable.setDataAfterLoading(result);
}
}
}

You can see that I added a class named TaskRunner, this will act as a handler-wrapper class. This class has a handler and an executor (Not like the pokemon) to handle thread work with the ThreadPool. The executeAsync is the function that calls the setUiForLoading() function that you don’t have to implement in case you have nothing to do. Afterward, the executor creates the RunnableTask(equivalent to AsyncTasks doInBackground). This runnable is a generic Runnable thanks to <R> tag.

The RunnableTask.run() function handles the call() function and returns its results so we can post it on to the handler to return to the UI thread and finally use the setDataAfterLoading(result) (equivalent to onPostExecute).

The basic idea behind this is to create a generic run with 3 main functions:

  • setUiForLoading() on main UI thread.
  • setDataAfterLoading(R result) on the main UI thread.
  • call() function that returns a generic result as an object that runs on a different thread.

If you’ve gotten this far — it’s not a long way to go.

In order to keep it as a simple implementation, I created this abstract base class:

public abstract class BaseTask<R> implements CustomCallable<R> {
@Override
public void setUiForLoading() {

}

@Override
public void setDataAfterLoading(R result) {

}

@Override
public R call() throws Exception {
return null;
}
}

Let’s create a class that extends the BaseTask<R> class:

public class NetworkTask extends BaseTask {

private final iOnDataFetched listener;//listener in fragment that shows and hides ProgressBar
public NetworkTask(iOnDataFetched onDataFetchedListener,) {
this.listener = onDataFetchedListener;
}

@Override
public Object call() throws Exception {

Object result = null;
result = someNetworkFunction();//some network request for example
return result;
}

@Override
public void setUiForLoading() {
listener.showProgressBar(); }

@Override
public void setDataAfterLoading(Object result) {
listener.setDataInPageWithResult(result); listener.hideProgressBar(); }
}
public interface iOnDataFetched{ void showProgressBar();
void hideProgressBar();
void setDataInPageWithResult(Object result);
}

Now in order for it to run we replace the following code in the fragment:

new myAsyncTask(this).execute();

With:

TaskRunner runner = new TaskRunner();

runner.executeAsync(new NetworkTask(this));

And ‘voua la’ the magic works. For each Async task that you want to replace all you had to do is create a function that extends the BaseTask, copy the code from your previous AsyncTask, and replace the call of the old AsyncTask, to the new TaskRunner and execute the new class.

By using this implementation you have to construct once the TaskRunner, BaseTask object, myInteface. After that you just need to create a new class that extends BaseTask and you’re done. The process is really easy to understand, debug, read and really low amount of code to re-write for each task you wish to create.

I had to come up with this solution for my workplace since we haven't moved to Kotlin yet. Like I said in the second paragraph, it’s better if you use Kotlins coroutines, but if you had no choice like me and you want to save yourself some writing for the future — enjoy these pieces of code.
Here is another article that converts the AsyncTask to Kotlins coroutines.

Thank you for reading.

--

--

Idan Damri
The Startup

Enthusiastic Android developer, as GEEK as they get..