Jake Wilson Jake Wilson - 5 months ago 17
Android Question

How to pass variables in and out of AsyncTasks?

I haven't spent much time working with AsyncTasks in Android. I'm trying to understand how to pass variables to and from the class. The syntax here:

class MyTask extends AsyncTask<String, Void, Bitmap>{

// Your Async code will be here

}


it's a little bit confusing with the
< >
syntax on the end of the class definition. Never seen that type of syntax before. It seems like I'm limited to only passing one value into the
AsyncTask
. Am I incorrect in assuming this? If I have more to pass, how do I do that?

Also, how do I return values from the AsyncTask?


It's a class and when you want to use it you call
new MyTask().execute()
but the actual method you use in the class is
doInBackground()
. So where do you actually return something?

Answer

Note: all of the information below is available on the Android Developers AsyncTask reference page. The Usage header has an example. Also take a look at the Painless Threading Android Developers Blog Entry.

Take a look at the source code for AsynTask.


The funny < > notation lets you customize your Async task. The brackets are used to help implement generics in Java.

There are 3 important parts of a task you can customize:

  1. The type of the parameters passed in - any number you want
  2. The type for what you use to update the progress bar / indicator
  3. The type for what you return once done with the background task

And remember, that any of the above may be interfaces. This is how you can pass in multiple types on the same call!

You place the types of these 3 things in the angle brackets:

<Params, Progress, Result>

So if you are going to pass in URLs and use Integers to update progress and return a Boolean indicating success you would write:

public MyClass extends AsyncTask<URL, Integer, Boolean> {

In this case, if you are downloading Bitmaps for example, you would be handling what you do with the Bitmaps in the background. You could also just return a HashMap of Bitmaps if you wanted. Also remember the member variables you use are not restricted, so don't feel too tied down by params, progress, and result.

To launch an AsyncTask instantiate it, and then execute it either sequentially or in parallel. In the execution is where you pass in your variables. You can pass in more than one.

Note that you do not call doInBackground() directly. This is because doing so would break the magic of the AsyncTask, which is that doInBackground() is done in a background thread. Calling it directly as is, would make it run in the UI thread. So, instead you should use a form of execute(). The job of execute() is to kick off the doInBackground() in a background thread and not the UI thread.

Working with our example from above.

...
myBgTask = new MyClass();
myBgTask.execute(url1, url2, url3, url4);
...

onPostExecute will fire when all the tasks from execute are done.

myBgTask1 = new MyClass().execute(url1, url2);
myBgTask2 = new MyClass().execute(urlThis, urlThat);

Notice how you can pass multiple parameters to execute() which passes the multiple parameter on to doInBackground(). This is through the use of varargs (you know like String.format(...). Many examples only show the extraction of the first params by using params[0], but you should make sure you get all the params. If you are passing in URLs this would be (taken from the AsynTask example, there are multiple ways to do this):

 // This method is not called directly. 
 // It is fired through the use of execute()
 // It returns the third type in the brackets <...>
 // and it is passed the first type in the brackets <...>
 // and it can use the second type in the brackets <...> to track progress
 protected Long doInBackground(URL... urls) 
 {
         int count = urls.length;
         long totalSize = 0;

         // This will download stuff from each URL passed in
         for (int i = 0; i < count; i++) 
         {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
         }

         // This will return once when all the URLs for this AsyncTask instance
         // have been downloaded
         return totalSize;
 }

If you are going to be doing multiple bg tasks, then you want to consider that the above myBgTask1 and myBgTask2 calls will be made in sequence. This is great if one call depends on the other, but if the calls are independent - for example you are downloading multiple images, and you don't care which ones arrive first - then you can make the myBgTask1 and myBgTask2 calls in parallel with the THREAD_POOL_EXECUTOR:

myBgTask1 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2);
myBgTask2 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urlThis, urlThat);

Note:

Example

Here is an example AsyncTask that can take as many types as you want on the same execute() command. The restriction is that each type must implement the same interface:

public class BackgroundTask extends AsyncTask<BackgroundTodo, Void, Void>
{
    public static interface BackgroundTodo
    {
        public void run();
    }

    @Override
    protected Void doInBackground(BackgroundTodo... todos)
    {
        for (BackgroundTodo backgroundTodo : todos)
        {
            backgroundTodo.run();

            // This logging is just for fun, to see that they really are different types
            Log.d("BG_TASKS", "Bg task done on type: " + backgroundTodo.getClass().toString());
        }
        return null;
    }
}

Now you can do:

new BackgroundTask().execute(this1, that1, other1); 

Where each of those objects is a different type! (which implements the same interface)

Comments