3

The API reference states,

AsyncTasks should ideally be used for short operations (a few seconds at the most.)

Is the problem with a doInBackground that takes, say, 30 seconds that the thread pool might run out of threads? And if that's the reason, would it cease to be a problem if I ensure my app will never have more than one such long running doInBackground executing concurrently?

Himanshu
  • 31,810
  • 31
  • 111
  • 133
arbee
  • 45
  • 1
  • 5
  • 2
    May I ask what you are trying to accomplish? – Joel Aug 31 '12 at 17:29
  • I have a screen with a bunch of links to remote audio samples, when the user clicks a link the relevant audio sample should play, playback lasts about 30 sec. Playback stops when the sample ends, or if the user clicks another link, or for a few other reasons. I was thinking to do all the MediaPlayer stuff in AsyncTask.doInBackground, and cancel the task if I need to stop playback prematurely. – arbee Aug 31 '12 at 17:48
  • You should use a service like Walter suggested. You should be using an async task for each audio download, but there is no need to spawn a thread to play the audio.. You can do all of the audio managing from the main thread of your application. – Joel Aug 31 '12 at 18:23

2 Answers2

6

The answer given by @Walter Mundt is correct. Nevertheless, I would like to add a complement of information and give a pointer to a library that can be used for long running AsyncTask.

AsyncTasks have been designed for doing stuff in background. And, yes, it's right that if your AsyncTask lasts for two long, then you will face 2 different issues :

  1. Activities are poorly tied to the activity life cycle and you won't get the result of your AsyncTask if your activity dies. Indeed, yes, you can but it will be the rough way.
  2. AsyncTask are not very well documented. A naive, though intuitive, implementation and use of an asynctask can quickly lead to memory leaks.

RoboSpice, the library I would like to introduce, as proposed by @Walter Mundt, uses a background service to execute this kind of requests. It has been designed for network requests (potentially long running by nature), but it could be easily adapted to execute just long running tasks, unrelated to network. I would be glad to add a patch to it.

Here is the reason why AsyncTasks are bad for long running tasks. The following reasonning is an adaptation from exerpts of RoboSpice motivations : the app that explains why using RoboSpice is filling a need on the Android platform.

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.

Progress of your task will be lost

You can use some workarounds to create a long running asynctask and manage its life cycle accordingly to the life cycle of the activity. You can either cancel the AsyncTask in the onStop method of your activity or you can let your async task finish, and not loose its progress and relink it to the next instance of your activity.

This is possible and we show how in RobopSpice motivations, but it becomes complicated and the code is not really generic. Moreover, you will still loose the progress of your task if the user leaves the activity and comes back. This same issue appears with Loaders, although it would be a simpler equivalent to the AsyncTask with relinking workaround mentionned above.

Using an Android service

The best option is to use a service to execute your long running background tasks. And that is exactly the solution proposed by RoboSpice. Again, it is designed for networking but could be extended to non-network related stuff. This library has a large number of features.

You can even get an idea of it in less than 30 seconds thanks to an infographics.


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.


If you are looking for an alternative to RoboSpice for non network related tasks (for instance without caching), you could also have a look at Tape.

Snicolas
  • 37,840
  • 15
  • 114
  • 173
5

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.

Walter Mundt
  • 24,753
  • 5
  • 53
  • 61
  • 1
    +1 from my side for the suggestion of using a **Service** . – Swayam Aug 31 '12 at 17:39
  • Thanks! For my particular use case I'd actually like to have my AsyncTask stop when the spawning activity stops. In this scenario, would AsyncTask.cancel be called (giving me a chance to explicitly release resources in onCancel)? – arbee Aug 31 '12 at 17:55
  • So when a user clicks one of your links, a second activity is launched that in turn launches an `AsyncTask?` You have to call `AsyncTask.cancel` for it to stop. On the second activity that launches the `AsyncTask` you could use `AsyncTask.onCancelled` to free resources. – Finding Nemo 2 is happening. Aug 31 '12 at 19:31
  • http://stackoverflow.com/questions/2531336/asynctask-wont-stop-even-when-the-activity-has-destroyed indicates that tasks may keep running when Activities are destroyed. I'm not sure if that applies when they are dropped for memory reasons; you could use the developer option to not keep background options to test that with a simple example. – Walter Mundt Sep 07 '12 at 05:23