Similar
to IntentService, AsyncTask allows us to run long running tasks in the
background. However, unlike IntentService the implementation is not
straight forward. One of the reason people might think is that it
involves generics ! There is no need to be afraid about those really. We
just need to know 2 important things. One is the type of data that will
be moving around and secondly these types cannot be primitive types
like int, float, boolean etc... Before going further into the
implementation, let me try to explain at a conceptual level.
Concept:
You
have a long task to do. You extend the AsycTask class and somewhere in
there, specify your task. Then, from the main thread, create an instance
of your extended class and call the execute() method. This should
somehow result in a background thread executing your task. Pretty simple
!
Now, there are 3 kinds of data that you will be dealing with:
1) Params : The parameters you pass on to the background task
2) Progress : The data units which the background task will be passing to you (UI thread) reporting its progress.
3) Result : The data that will be returned by the background task (to the UI thread) on completion.
Clearly
there seems to be a communication going on between the UI thread and
the worker thread. Hence, I like to visualize the AsyncTask as follows:
All
the methods mentioned in the diagram will be a part of the extended
AsyncTask class but however will run on different threads as indicated.
1) You have an instance of the AsyncTask and will call the execute()
method. This method runs on the UI thread and serves as a trigger to the
platform to spawn a worker thread.
2) Before handling the control to the worker thread, the platform will
execute the onPreExecute() method on the main thread. We can perform any
necessary task setup.
3) After this method finishes, the platform executes the doInBackground() method on the worker thread.
4,5) Now, whenever the worker thread calls the publishProgress() method,
the platform calls the onProgressUpdate() method on the main thread
with the parameters passed to the publishProgress() method.
6,7) Finally when the long running task is over i.e. the
doInBackground() method returns, the return value is passed to the
onPostExecute() method.
And, for all this to happen our AsyncTask class needs to know the datatypes for the 3 kinds of data discussed above.
It is important to note that just like IntentService, AsycTask also
spawns a SINGLE worker thread and hence only one task can be run in
background. Furthermore, once an instance of our AsyncTask is created,
you can call the execute() method on it only ONCE. An exception will be
thrown if you try to call execute() on the same instance of AsyncTask
more than once. Hence, with AsyncTask if you want to run multiple tasks
in background, create multiple instances and call execute on them.
Note:
API level 11 (Honeycomb) adds the executeOnExecutor() method which
enables to run multiple tasks on a pool of threads managed by AsyncTask
class. I have not studied and tried this. I will update the post (or
write a new one) once I try this out.
Implementation details:
Extend the AsyncTask class and pass to it the datatypes for Params, Progress and Result
class TestAsyncTask extends AsyncTask {
}
So, here, we are telling that our worker thread will accept String
parameters as input, the worker thread may publish the progress to the
main thread as an Integer value and finally the return value of the
doInBackground method will be an Integer. Note that the Integer class is
used instead of primitive data type int.
Now, It may happen that, you have a worker thread that doesn't need any
input, doesn't wish to convey any progress or any result. In that case,
the AsyncTask definition will look like:
class TestAsyncTask extends AsyncTask{
}
Okay. Sticking with the first example, lets add in the doInBackground, onProgressUpdate and onPostExecute methods.
class TestAsyncTask extends AsyncTask {
protected Integer doInBackground(String... strings) {
// Long running task - say processing of each passed string
for (int i = 0; i < strings.length; i++){
// Do processing of strings[i]
publishProgress(i); // publish that ith string is processed
}
return 0;
}
protected void onProgressUpdate(Integer... item){
Log.d("TestP", item[0] + " item has been processed");
}
protected void onPostExecute(Integer result){
Log.d("TestP", "AsyncTask returned : " + result);
}
}
From the main thread (say from your Activity or service), create an instance of the AsyncTask.
TestAsyncTask myATask = new TestAsyncTask();
myATask.execute("one", "two", "three", "four");
As mentioned before, if you want to run multiple AsyncTasks in parallel, just create another instance and call execute on it.
TestAsyncTask myATask2 = new TestAsyncTask();
myATask2.execute("five", "six", "seven", "eight");
There are 4 threading rules we need to follow for AsyncTask to work properly. These are mentioned in the AsyncTask
documentation, but I'll mention those again so that everything is available at one place.
1. The task instance needs to be created on the main thread.
2. The execute() method needs to be called on the main thread.
3. Do not explicitly call any of the preExecute(), postExecute(), doInBackground() and onProgressUpdate() methods.
4. The task can be executed only once on one instance.
Thats all you need to know to execute AsyncTask.
I'm pretty much excited about my next post. I'm planning to do a
comparison of various background mechanisms that we have seen till now
as well as try to come up with some guidelines. I'm still working on it,
hope to post them soon. Stay tuned !