19

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:

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?

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Jake Wilson
  • 88,616
  • 93
  • 252
  • 370
  • you should probably look at some examples and android docs for this to get better idea. – Lalit Poptani Mar 28 '12 at 05:08
  • I have, the doc's don't say anything about passing multiple objects to AsyncTask. – Jake Wilson Mar 28 '12 at 05:47
  • I just realized that since it's a class that I can probably just create public members and set those members to whatever I want before `.execute()`... I think that will work. – Jake Wilson Mar 28 '12 at 05:50
  • Ok if you are confused about passing multiple objects then you can check my answer. – Lalit Poptani Mar 28 '12 at 05:57
  • You can have a look at [this post](http://stackoverflow.com/a/7618705/593709) also.. may be of some sort of help for you.. – Adil Soomro Mar 28 '12 at 10:13
  • 1
    You really need to read this tutorial - [Ultimate Guide - Android AsyncTask Example too](https://androidride.com/asynctask-android-tutorial-example/) – Athira Reddy Apr 18 '19 at 02:17

5 Answers5

48

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)

Peter Ajtai
  • 56,972
  • 13
  • 121
  • 140
  • Very comprehensive, thanks. Although I was looking for a way to pass multiple types to the task. – Jake Wilson Mar 28 '12 at 14:41
  • @Jakobud - yes, the problem is that a supertype cannot implement a wildcard (`?`). However, what I would do instead is make an AsyncTask for each type. Since there probably would be code duplication, you can create a common base class to inherit from. Also, you *could* just pass in plain old `Object` and use `instanceof` to cast appropriately... though, this is probably not a good solution, since you are fighting Java at that point. - Finally, you can set the params as `Void` and use custom member fields instead. These could be passed in to the object using a different method for each type. – Peter Ajtai Mar 28 '12 at 16:49
  • Can you simply create a constructor for the AsynTask that takes whatever parameters you want? Or does this class not really work that way? The solution I am trying right now that seems to work fine is creating public class members and setting those members to something after creating the AsyncTask but before `.execute()`. Is that a bad approach? It seems to work good so far... – Jake Wilson Mar 28 '12 at 17:13
  • @Jakobud - Yes, you can make the constructor whatever you want. That's a good place to pass in values for class members. You can also make setter methods to set those class members (so they're not public - this will increase your code's flexibility). It's basically a class like any other. – Peter Ajtai Mar 28 '12 at 17:17
  • Okay thanks for the help. One weird thing I don't get though. The first variable in `` defines the type of object passed into the class. If you define a constructor that takes a bunch of different random object types, whats the point of the ` – Jake Wilson Mar 28 '12 at 19:14
  • @Jakobud - The point of the first type - String - in your example, is to let `execute()` know what type you will be passing to it. You can use `Void` if you will not be passing anything, since you set things up earlier. `execute()` is still useful in this case, since it starts things off.... ` – Peter Ajtai Mar 28 '12 at 19:41
  • @Jakobud - Oh, I just realized you could use Interfaces to easily pass in multiple type on `execute()`. See the note at the bottom of the answer. – Peter Ajtai Mar 28 '12 at 21:05
  • I'm not familiar with interfaces in Java but I will look into that as well. Thanks again – Jake Wilson Mar 28 '12 at 21:26
9

I recognize that this is a late answer, but here's what I've been doing for the last while.

When I'm needing to pass in a bunch of data to an AsyncTask, I can either create my own class, pass that in and then access it's properties, like this:

public class MyAsyncTask extends AsyncTask<MyClass, Void, Boolean> {

    @Override
    protected Boolean doInBackground(MyClass... params) {

        // Do blah blah with param1 and param2
        MyClass myClass = params[0];

        String param1 = myClass.getParam1();
        String param2 = myClass.getParam2();

        return null;
    }
}

and then access it like this:

AsyncTask asyncTask = new MyAsyncTask().execute(new MyClass());

or I can add a constructor to my AsyncTask class, like this:

public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {

    private String param1;
    private String param2;

    public MyAsyncTask(String param1, String param2) {
        this.param1 = param1;
        this.param2 = param2;
    }

    @Override
    protected Boolean doInBackground(Void... params) {

        // Do blah blah with param1 and param2

        return null;
    }
}

and then access it like this:

AsyncTask asyncTask = new MyAsyncTask("String1", "String2").execute();

Hope this helps!

LukeWaggoner
  • 8,869
  • 1
  • 29
  • 28
2

Since you can pass array of objects in the square bracket, that is the best way to pass data based on which you want to do processing in the background.

You could pass the reference of your activity or the view in the Constructor and use that to pass data back into your activity

class DownloadFilesTask extends AsyncTask<URL, Integer, List> {
    private static final String TAG = null;
    private MainActivity mActivity;
    public DownloadFilesTask(MainActivity activity) {
        mActivity = activity;
        mActivity.setProgressBarIndeterminateVisibility(true);
    }

    protected List doInBackground(URL... url) {
        List output = Downloader.downloadFile(url[0]);
        return output;
    }

    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    private void setProgressPercent(final Integer integer) {
        mActivity.setProgress(100*integer);
    }

    protected void onPostExecute(List output) {

        mActivity.mDetailsFragment.setDataList((ArrayList<Item>) output);

        //you could do other processing here
    }
}
Rajdeep Dua
  • 11,190
  • 2
  • 32
  • 22
  • I suppose that would work if I was wanting to send multiple variables of the same type, like an array of Strings. In my case I need to pass a custom object along with a HashMap of parameters... I get the feeling I should be using something other than AsyncTask... – Jake Wilson Mar 28 '12 at 05:46
  • You can send an array of CustomObjects too – Rajdeep Dua Mar 28 '12 at 08:21
0

Passing a simple String:

 public static void someMethod{ 
     String [] variableString= {"hello"};
     new MyTask().execute(variableString);
}

static class MyTask extends AsyncTask<String, Integer, String> {

        // This is run in a background thread
        @Override
        protected String doInBackground(String... params) {
            // get the string from params, which is an array
            final String variableString = params[0];

            Log.e("BACKGROUND", "authtoken: " + variableString);

            return null;
        }
    }
Afshin Ghazi
  • 2,784
  • 4
  • 23
  • 37
0

Alternatively, you could just use a regular thread and usea handler to send data back to the ui thread by overriding the handlemessage function.

Kevin
  • 532
  • 2
  • 7