Android Asynctask for Long Running Operations

Android AsyncTask for long running operations

It is a very good question, it takes time as an Android Programmer to fully understand the issue. Indeed AsyncTask have two main issues that are related :

  • They are poorly tied to the activity life cycle
  • They create memory leaks very easily.

Inside the RoboSpice Motivations app (available on Google Play) we answer that question in detail. It will give an in-depth view of AsyncTasks, Loaders, their features and drawbacks and also introduce you to an alternative solution for network requests : RoboSpice.
Network requests are a common requirement in Android and are by nature long running operations
.
Here is an excerpt from the app :

The AsyncTask and Activity life cycle


AsyncTasks don't follow Activity instances' life cycle. If you start an AsyncTask inside an Activity and you rotate the device, the Activity will be destroyed and a new instance will be created. But the AsyncTask will not die. It will go on living until it completes.


And when it completes, the AsyncTask won't update the UI of the new Activity. Indeed it updates the former instance of the activity that
is not displayed anymore. This can lead to an Exception of the type java.lang.IllegalArgumentException: View not attached to window manager if you
use, for instance, findViewById to retrieve a view inside the Activity.

Memory leak issue


It is very convenient to create AsyncTasks as inner classes of your Activities. As the AsyncTask will need to manipulate the views
of the Activity when the task is complete or in progress, using an inner class of the Activity seems convenient : inner classes can
access directly any field of the outer class.


Nevertheless, it means the inner class will hold an invisible reference on its outer class instance : the Activity.


On the long run, this produces a memory leak : if the AsyncTask lasts for long, it keeps the activity "alive"
whereas Android would like to get rid of it as it can no longer be displayed. The activity can't be garbage collected and that's a central
mechanism for Android to preserve resources on the device.


It is really a very very bad idea to use AsyncTasks for long running operations. Nevertheless, they are fine for short living ones such as updating a View after 1 or 2 seconds.

I encourage you to download the RoboSpice Motivations app, it really explains this in-depth and provides samples and demonstrations of the different ways to do some background operations.

What do you use for long running tasks since Loaders and AsyncTask have been deprecated?

For performing network requests, the network library that you are using should handle that. All network SDKs (Okhttp, Retrofit) handles the background processing, you don't have to worry.

For other background processes or if you are using your own code to make the HTTP calls like HttpUrlConnection etc, you have a couple of options.

  1. Coroutines. If your project is in Java, that won't be a problem because Java and Kotlin are interoperable.

  2. ExecutorService: Provides a bunch of useful APIs to push a task into the background and then you can use a MainThread Looper to send the data back from background thread to UI.

  3. Handler threads and Intent service: They are standard android APIs which are made for background processing only.

Reference: https://developer.android.com/guide/background

Should or should not AsyncTask be used for long-running operations?

the interface exposed by AsyncTask defines a setup, a background operation, and an "on done" callback that can update the UI. this implies that it's intended use is for operations that have a clearly defined finish that results in feedback to the user.

if you need an ongoing background thread, use an Executor to execute an instance of Runnable that hosts your ongoing operations. use this with care however, since when your app leaves the foreground, the thread will continue to run. carefully craft your Runnable such that it can be aborted (the run() method can be caused to return cleanly).

it's worth it to note that AsyncTask just forces you into a well-defined pattern for performing an asynchronous operation then updating the UI. IMHO it's overly complicated and makes a lot of assumptions, like a single-or limited size thread pool. for example, if one part of your app was blocking in AsyncTasks, would you assume that it would prevent an AsyncTask from running in another part of your app? it will.

What to use instead of AsyncTask to update UI for long running operations

You can use a thread:

new Thread(new Runnable(){
@Override
public void run(){
//do operation
//make and show notifications. Notifications are thread safe
}
}});

You can notify from the worker thread because the notification does not go through your apps process. See here

If you have to post to your app's UI thread, then you can use a handler like so:

//put this in the UI thread
Handler handler = new Handler();
//then within the thread code above
handler.post(new Runnable(){
@Override
public void run(){
//code that you want to put on the UI thread, update views or whatever
}
}});

Edit: Instead of bothering with Handler, you can use runOnUiThread(...) from your worker thread.

AsyncTask for longer than a few seconds?

I believe that AyncTasks are in general still tied to the foreground activity stack that spawned them, so that e.g. if an Activity spawns an AsyncTask, the user leaves the app, and then the OS is short of memory, it will kill the Activity's process (including the still-running AsyncTask), and just expect you to restore the state and start over if the user resumes/returns to your app.

For longer-running tasks, particularly the sort where there will only be only one or a few, you probably want a Service instead, because those can persist even when your app's UI is shut down to save memory.

Disclaimer: I haven't done Android coding in awhile, so this answer may be out of date or based on a flawed understanding of how things work. I will remove this caveat if someone with more recent experience can comment to confirm; high-rep folks are welcome to just edit this paragraph away instead if they know this is correct.

android:AsyncTask for background operations

It is really a very very bad idea to use AsyncTasks for long running operations. Nevertheless, they are fine for short living ones such as updating a View after 1 or 2 seconds.

AsyncTasks don't follow Activity instances' life cycle. If you start an AsyncTask inside an Activity and you rotate the device, the Activity will be destroyed and a new instance will be created. But the AsyncTask will not die. It's one of the reasons that it is advised to avoid AsyncTask for long running tasks.

However if your Activity goes on pause during the process, this is likely to crash so you should cancel your task in onPause() using task.cancel(true);

If you need your task to survive your activity, then you should use either a Service or a ServiceIntent. The former is a bit more complex as you have to manage its life-cycle but offers more flexibility. The second is damn straightforward so I can give you an example here:

public class MyIntentService extends IntentService{

public MyIntentService() {
super("MyIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {
// Background work in a worker thread (async)

// You could also send a broadcast if you need to get notified
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("whatever");
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
}
}

The you can start if from your activity with this command:

Intent i = new Intent(context, MyIntentService.class);
context.startService(i);

Here is some links about Intent Service:

  1. http://javatechig.com/android/creating-a-background-service-in-android
  2. http://www.101apps.co.za/articles/using-an-intentservice-to-do-background-work.html
  3. http://www.mysamplecode.com/2011/10/android-intentservice-example-using.html

Hope this will help you.



Related Topics



Leave a reply



Submit