There were days when async task in Android was really very great. They allowed us to perform operations in a non thread blocking manner. They were great and not so great at the same time ( stares Schrödinger(ly) ). But now as of Android 11 ( API 30 ) they have been deprecated.
But fear not, we have really cool thing now. Yes, i’m talking about co-routines. For those of you who have never used an async task, see how it looks like below.
Deprecated means it is no longer deemed suitable because of performance, security or technological reasons.
Quick Navigation
Quick Snippet
If you don’t have time to read the whole article. Here is a simple coroutine code for you to quickly do accomplish things.
First you have to include this in the app
> build.gradle
file. Then, sync it.
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
Code language: JavaScript (javascript)
After that create a file named AsyncTaskAlternative
. And copy paste the following snippet of code.
object AsyncTaskAlternative {
fun doInBackground(processing: () -> T?, callback: (data: T?) -> Unit) {
GlobalScope.launch(Dispatchers.IO) {
// do long running task here
val result = processing.invoke()
withContext(Dispatchers.Main) {
// change the thread to main and execute the callback
callback.invoke(result)
}
}
}
}
Code language: Kotlin (kotlin)
Now the only thing left to do is use it in your class. A simple Kotlin implementation is shown below.
AsyncTaskAlternative.doInBackground({
// do your processing here
val myTask = "hello-there"
// you have to return the result here which will be passed to the block below
myTask // pass null if you don't want to return anything
}, {
// result will be here; it can be null if you don't want to return anything
// don't worry about generating background thread exception it is already handled.
})
Code language: JavaScript (javascript)
We have used coroutines and higher order functions in the above example. If you want to learn more there will be a post available soon.
What is Async Task
In Java an AsyncTask
implementation looks something like below.
class AsyncTaskExample extends AsyncTask<Void, Void, Void> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... voids) {
return null;
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
Code language: Java (java)
The onPreExecute()
block is executed before doInBackground(Void… voids)
is triggered. And when the doInBackground(Void… voids)
is complete onPostExecute(Void aVoid)
block is invoked by the system. Which made it really easy to do asynchronous operations.
Now, we left out the onProgressUpdate(Void… values)
block of code. This block was executed when there was some ongoing tasks such as downloading which required update more frequently. These all worked in to make sure you get proper async operation feeling. But they async tasks in android had a great problem that behaved in a weird manner sometimes. Making them really problematic as per the answers on SO.
But for Me I only had one problem with async tasks i.e. if the task was running for long period of time, U.I thread froze. Some of you might say that it’s very unlikely, but it happened. But then again in the documentation it is clearly mentioned that you should not use async task for operation that takes longer than couple of seconds.
Also Read: ZOOM SDK Android: Learn How to Create an Online Meeting
TECHENUM
Why is Async Task in Android deprecated ?
There were too many problems with Async Task in Android. There were context leaks, crashes on configuration changes. As the async task was not tied to Lifecycle of the Activity or Fragment.
If you were not careful with your decision on implementing the Async Task. You would end up with random crashes at unexpected places. Which is always a bad thing for the user and developer as a whole. Nobody likes inconsistent states right ?
Let us look at some of the alternatives of Async Task.
3 best approaches
Great that i’m making async task look like a villain, which was never my intention. And all i’m saying is that there are better ways to achieve these things. Here I will outline exactly three ways how one might accomplish asynchronous operation and more. This will get really very cool if you are already familiar with Kotlin. But even if you’re not it’ll be no problem.
Handler Thread
What is a HandlerThread
? The answer to the question is simple. The HandlerThread
is a wrapper class for Thread which allows us to create new threads with more ease. It is because some boiler plate is handled by android system itself.
Let us look at the initialization and usage of the handler thread.
In the code below we have created a new instance of HandlerThread
on LINE 1 by passing a name "MyHandlerThread
” and on LINE 2 we have started this thread.
HandlerThread ht = new HandlerThread("MyHandlerThread");
ht.start();
Code language: Java (java)
After we have created a thread and initialized it we need to use a Handler
and a Runnable
class to utilize out new thread as under.
Handler handler = new Handler(ht.getLooper());
Runnable runnable = new Runnable() {
@Override
public void run() {
// your async code goes here.
}
};
handler.post(runnable);
Code language: Java (java)
See the comment in LINE 5
? Yes that’s where you put your code which will be executed in a separate thread. Let us improve it a little more with example.
In the code below you retrieve the result as Object
in LINE 8
. And you pass it from LINE 22
. And we have invoked another method on LINE 9 passing the response
variable. The doSomethingOnUi()
method has necessary code to run things on UI.
public void doSomeTaskAsync() {
HandlerThread ht = new HandlerThread("MyHandlerThread");
ht.start();
Handler asyncHandler = new Handler(ht.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Object response = msg.obj;
doSomethingOnUi(response);
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
// your async code goes here.
try {
Thread.sleep(10_000);
// create message and pass any object here doesn't matter
// for a simple example I have used a simple string
Message message = new Message();
message.obj = "My Message!";
asyncHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
asyncHandler.post(runnable);
}
Code language: Java (java)
And now let us look at the method doSomethingOnUi()
. After 10 milliseconds
we will get the string "My Message!"
. In the code below cast the object to whatever you had passed in LINE 22
above. The code in the run()
will be executed on main thread. That is where you must put your code for updating U.I elements.
private void doSomethingOnUi(Object response) {
Handler uiThread = new Handler(Looper.getMainLooper());
uiThread.post(new Runnable() {
@Override
public void run() {
// now update your UI here
// cast response to whatever you specified earlier
}
});
}
Code language: Java (java)
That was all about HandlerThread
let us move forward into Executors
and see how same thing can be done using Executors
below. We know one way to replace async task in Android now.
You should move this line
HandlerThread ht = new HandlerThread("MyHandlerThread");
Code language: JavaScript (javascript)
Out of the method and create an instance only once like a singleton.
Also Read: Implement SMS Broadcast Receiver in Android
TECHENUM
Executors
Executors are way more structured service which manage multiple number ( pool ) of threads. We can specify how many threads are to be kept in the memory. If we supply more than the amount of acceptable pool of threads, they will be kept in a queue.
Everything is exactly same except the LINE 2
and the try {} catch {}
block of code. And you may want to make sure you create only one instance of ExecutorService
.
.public void doSomeTaskAsync() {
ExecutorService executors = Executors.newFixedThreadPool(1);
Runnable runnable = new Runnable() {
@Override
public void run() {
// your async code goes here.
try {
Thread.sleep(10_000);
// create message and pass any object here doesn't matter
// for a simple example I have used a simple string
String msg = "My Message!";
doSomethingOnUi(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executors.submit(runnable);
}
Code language: Java (java)
The other method is exactly same though it might be a better thing to use String
instead of Object
( because we used String
in our example ). The second block of code:
private void doSomethingOnUi(Object response) {
Handler uiThread = new Handler(Looper.getMainLooper());
uiThread.post(new Runnable() {
@Override
public void run() {
// now update your UI here
// cast response to whatever you specified earlier
}
});
}
Code language: Java (java)
This is just a quick way on using ExecutorService
, I have not really explained it in detail here. For that I might write a separate article later. Now that takes us to our final way of doing async operation i.e. co-routine
.
Make sure to move the line out of the method as we should not create a new instance each time the method is invoked.
ExecutorService executors = Executors.newFixedThreadPool(1);
Coroutines
Do you want to learn more about coroutine basics ? Read more here : Kotlin Coroutine in Android : Understanding the Basics
co-routine
cannot be used with Java
it’s Kotlin
specific feature. We can achieve something similar in Java
with the help of Future
. But I will not be discussing about future in the present. Let’s dive right into co-routine
.
Let us quickly convert the above code into co-routines
and see how Kotlin
makes it really very easy for us.
In the code below we have used CoroutineExceptionHandler
to handle any exceptions when executing co-routine
. the code inside the withContext(Dispatchers.IO) { }
is where the magic happens. You might notice Dispatchers.IO
which is used by co-routine
to identify the type of task. If you’re familiar with RxJava
you might notice the similarity.
fun doSomeTaskAsync() {
val errorHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
// here the exception is gracefully handled
}
GlobalScope.launch(errorHandler) {
var result: String = ""
withContext(Dispatchers.IO) {
delay(10_000)
result = "My Task!"
doSomethingOnUi(result)
}
}
}
Code language: Kotlin (kotlin)
Now for the U.I update part it’s really easy use the code below. If you’re not familiar to Kotlin the keyword suspend
will make your heart pound a bit, read more.
suspend fun doSomethingOnUi(msg: String) {
withContext(Dispatchers.Main) {
// update your UI code here
}
}
Code language: Kotlin (kotlin)
Now that we have successfully understood how we might replace async task in android. There might be other ways to explore which I might have missed. But I play with co-routines
and ExecutorService
all the time in my work. If you find any problem with the code examples please feel free to leave a comment below.
Also Read: Interface in OOP: Guide to Polymorphism and Callbacks
TECHENUM
Thanks!