Is AsyncTask really conceptually flawed or am I just missing something?
How about something like this:
class MyActivity extends Activity {
Worker mWorker;
static class Worker extends AsyncTask<URL, Integer, Long> {
MyActivity mActivity;
Worker(MyActivity activity) {
mActivity = activity;
}
@Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... progress) {
if (mActivity != null) {
mActivity.setProgressPercent(progress[0]);
}
}
@Override
protected void onPostExecute(Long result) {
if (mActivity != null) {
mActivity.showDialog("Downloaded " + result + " bytes");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWorker = (Worker)getLastNonConfigurationInstance();
if (mWorker != null) {
mWorker.mActivity = this;
}
...
}
@Override
public Object onRetainNonConfigurationInstance() {
return mWorker;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mWorker != null) {
mWorker.mActivity = null;
}
}
void startWork() {
mWorker = new Worker(this);
mWorker.execute(...);
}
}
Android AsyncTask returns when Activity that executed it... is gone
A very common hurdle in Android development. When I first stumbled on this issue, I found this Q with a lot of great discussion that illuminated things for me:
Is AsyncTask really conceptually flawed or am I just missing something?
It is imperative on you, the author of the AsyncTask, to ensure that it's orphaned completion is 'safe' or else to cancel/abort. The conventional wisdom is to cancel (or at least logically dissociate via some mechanism of your own rolling, as @iagreen proposes) the Task instance from within your Activity's onPause or onDestroy.
Ideal way to cancel an executing AsyncTask
There is frequent guidance around the web from Android team members that AsyncTask is intended to be for short operations whose lifecycle closely matches the 'intended' lifecycle of its parent activity, so if you find yourself doing longrunning operations and having to resume partial progress, etc you might want to look at explicit thread management or coding up a Service, etc:
http://developer.android.com/reference/android/app/Service.html
What can cause a AsyncTask not to execute?
Sadly AsyncTask
is not suitable for critical code execution since, depending on the ThreadPool
base and max size, your AsyncTask may never execute.
Moreover, the onPostExecute
method could be called when the Activity
it is referring (i.e. its creating context) has already been destroyed. You have no way to synchronize with it rather then maybe using join() on the AsyncThread
from the UI Thread.
Even though I've seen doing this also in the Android Camera App it isn't a good idea to block the UI Thread waiting for an event since you coulg get an ANR (Application Not Running) notification.
Take a look at this: Is AsyncTask really conceptually flawed or am I just missing something?
Consider using IntentServices
, HandlerThread
or ThreadPoolExecutors
if you need a possibly better way to synchronize your worker thread with your your UIThread.
From http://developer.android.com/training/run-background-service/create-service.html:
Also, an IntentService isn't affected by most user interface lifecycle events, so it continues to run in circumstances that would shut down an AsyncTask
AsyncTask won't stop even when the Activity has destroyed
The answer given by @Romain Guy is correct. Nevertheless, I would like to add a complement of information and give a pointer to a library or 2 that can be used for long running AsyncTask and even more for network oriented asynctasks.
AsyncTasks have been designed for doing stuff in background. And yes, you can stop it using the cancel
method. As you download stuff from the Internet, I strongly suggest you take care of your thread when it is the IO blocking state. You should organize your download as follow :
public void download() {
//get the InputStream from HttpUrlConnection or any other
//network related stuff
while( inputStream.read(buffer) != -1 && !Thread.interrupted() ) {
//copy data to your destination, a file for instance
}
//close the stream and other resources
}
Using the Thread.interrupted
flag will help your thread to quit properly a blocking io state. Your thread will be more responsive to an invocation of the cancel
method.
AsyncTask design flaw
But if your AsyncTask lasts for too long, then you will face 2 different issues :
- 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.
- 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, uses a background service to execute this kind of requests. It has been designed for network requests. It provides additional features such as automatic caching of requests' results.
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 network related stuff.
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.
What happens on Activity.finish() with AsyncTask still running in background?
I've tried the same thing with the Thread, and my observation is it keeps running the thread.
Related Topics
Android Get Free Size of Internal/External Memory
How to Disable Landscape Mode in Android
How to Find Serial Number of Android Device
How to Send String from One Activity to Another
Difference and Uses of Oncreate(), Oncreateview() and Onactivitycreated() in Fragments
How to Add New Contacts in Android
Manifest Merger Failed:Uses-Sdk:Minsdkversion 14
How to Use Opencv in Using Gradle
Using Build Flavors - Structuring Source Folders and Build.Gradle Correctly
How to Add a Jar in External Libraries in Android Studio
Why Does Layoutinflater Ignore the Layout_Width and Layout_Height Layout Parameters I'Ve Specified
Intercepting Links from the Browser to Open My Android App
How to Get Current Time from Internet in Android
How to Animate Recyclerview Items When They Appear
Android: How to Bind Spinner to Custom Object List
Disabling the Fullscreen Editing View for Soft Keyboard Input in Landscape