270

I'm trying to run two AsyncTasks at the same time. (Platform is Android 1.5, HTC Hero.) However, only the first gets executed. Here's a simple snippet to describe my problem:

public class AndroidJunk extends Activity {
 class PrinterTask extends AsyncTask<String, Void, Void> {
     protected Void doInBackground(String ... x) {
      while (true) {
       System.out.println(x[0]);
       try {
        Thread.sleep(1000);
       } catch (InterruptedException ie) {
        ie.printStackTrace();
       }
      }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        new PrinterTask().execute("bar bar bar");
        new PrinterTask().execute("foo foo foo");

        System.out.println("onCreate() is done.");
    }
}

The output I expect is:

onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo

And so on. However, what I get is:

onCreate() is done.
bar bar bar
bar bar bar
bar bar bar

The second AsyncTask never gets executed. If I change the order of the execute() statements, only the foo task will produce output.

Am I missing something obvious here and/or doing something stupid? Is it not possible to run two AsyncTasks at the same time?

Edit: I realized the phone in question runs Android 1.5, I updated the problem descr. accordingly. I don't have this problem with an HTC Hero running Android 2.1. Hmmm ...

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
rodion
  • 6,087
  • 4
  • 24
  • 29
  • Your code works for me, so the problem has to be somewhere else. Did you enter a filter in your LogCat view? ;-) – mreichelt Nov 01 '10 at 13:29
  • Hm, that's odd. I don't have any filtering in logcat. Are you using 1.6 as well? If so, which phone? – rodion Nov 01 '10 at 13:34
  • Oops, just realized it's running (ancient) Android 1.5 – rodion Nov 01 '10 at 13:36
  • 1
    I used Android 1.6 as the target and an Android 2.1 emulator. So if the problem really occurs on a HTC Hero with Android 1.5 only - screw them, you're fine. ;-) HTC Hero already has the update to a newer Android version. I wouldn't bother about it if there are some manufacturers which screw things up. In addition I wouldn't mind about Android 1.5 any more. – mreichelt Nov 01 '10 at 13:48
  • AsyncTask should be used for shorter duration tasks of 5 ms. Move to ThreadPoolExecutor (https://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html). Related post: https://stackoverflow.com/questions/6964011/handler-vs-asynctask-vs-thread – Ravindra babu Sep 26 '17 at 09:59

7 Answers7

442

AsyncTask uses a thread pool pattern for running the stuff from doInBackground(). The issue is initially (in early Android OS versions) the pool size was just 1, meaning no parallel computations for a bunch of AsyncTasks. But later they fixed that and now the size is 5, so at most 5 AsyncTasks can run simultaneously. Unfortunately I don't remember in what version exactly they changed that.

UPDATE:

Here is what current (2012-01-27) API says on this:

When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. After HONEYCOMB, it is planned to change this back to a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the executeOnExecutor(Executor, Params...) version of this method with THREAD_POOL_EXECUTOR; however, see commentary there for warnings on its use.

DONUT is Android 1.6, HONEYCOMB is Android 3.0.

UPDATE: 2

See the comment by kabuko from Mar 7 2012 at 1:27.

It turns out that for APIs where "a pool of threads allowing multiple tasks to operate in parallel" is used (starting from 1.6 and ending on 3.0) the number of simultaneously running AsyncTasks depends on how many tasks have been passed for execution already, but have not finished their doInBackground() yet.

This is tested/confirmed by me on 2.2. Suppose you have a custom AsyncTask that just sleeps a second in doInBackground(). AsyncTasks use a fixed size queue internally for storing delayed tasks. Queue size is 10 by default. If you start 15 your custom tasks in a row, then first 5 will enter their doInBackground(), but the rest will wait in a queue for a free worker thread. As soon as any of the first 5 finishes, and thus releases a worker thread, a task from the queue will start execution. So in this case at most 5 tasks will run simultaneously. However if you start 16 your custom tasks in a row, then first 5 will enter their doInBackground(), the rest 10 will get into the queue, but for the 16th a new worker thread will be created so it'll start execution immediately. So in this case at most 6 tasks will run simultaneously.

There is a limit of how many tasks can be run simultaneously. Since AsyncTask uses a thread pool executor with limited max number of worker threads (128) and the delayed tasks queue has fixed size 10, if you try to execute more than 138 your custom tasks the app will crash with java.util.concurrent.RejectedExecutionException.

Starting from 3.0 the API allows to use your custom thread pool executor via AsyncTask.executeOnExecutor(Executor exec, Params... params) method. This allows, for instance, to configure the size of the delayed tasks queue if default 10 is not what you need.

As @Knossos mentions, there is an option to use AsyncTaskCompat.executeParallel(task, params); from support v.4 library to run tasks in parallel without bothering with API level. This method became deprecated in API level 26.0.0.

UPDATE: 3

Here is a simple test app to play with number of tasks, serial vs. parallel execution: https://github.com/vitkhudenko/test_asynctask

UPDATE: 4 (thanks @penkzhou for pointing this out)

Starting from Android 4.4 AsyncTask behaves differently from what was described in UPDATE: 2 section. There is a fix to prevent AsyncTask from creating too many threads.

Before Android 4.4 (API 19) AsyncTask had the following fields:

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(10);

In Android 4.4 (API 19) the above fields are changed to this:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

This change increases the size of the queue to 128 items and reduces the maximum number of threads to the number of CPU cores * 2 + 1. Apps can still submit the same number of tasks.

Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
Vit Khudenko
  • 28,288
  • 10
  • 63
  • 91
  • This explains the behavior. When I use Threads and a handler to pass results to the UI, it works fine. – rodion Nov 02 '10 at 15:01
  • 11
    Just FYI, core pool size is 5, but max is 128. This means that if you fill up your queue, more than 5 (up to 128) can run simultaneously. – kabuko Mar 07 '12 at 01:27
  • 2
    @kabuko: you're absolutely right. Just investigated this on 2.2 and confirming that if 5 tasks already running (core pool size is 5), then it starts putting the rest of tasks into the queue of delayed tasks, however if the queue is already full (the max queue size is 10), then it starts an additional worker thread for the task so the task starts immediatelly. – Vit Khudenko Mar 10 '12 at 21:46
  • What a mess... Already crying for the number of lines to manage all android versions. Thanks for detailed input! – Thibault D. Mar 26 '13 at 10:54
  • Hi Arhimed, thanks for the consistent update on this thread. I have a question, in AsyncTask.THREAD_POOL_EXECUTOR's definition, I could see there's only 1 second for the excessive idle task to keep alive, do you have any experience encountering issue caused by this ? thanks http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/os/AsyncTask.java#AsyncTask.0THREAD_POOL_EXECUTOR – qiuping345 Aug 05 '15 at 23:09
  • @qiuping345 any possible effect (thread GC/instantiation has its price) depends on how frequently you start new tasks and how long they are. In my practice there was no such situation when the `keep alive` caused me any substantial issues. If 1 sec does not suit your case (or you feel like that), then you can use your custom Executor (configured by you) for testing your ideas. – Vit Khudenko Aug 08 '15 at 18:33
  • 2
    Please update this answer for the [new compat functions](http://developer.android.com/reference/android/support/v4/os/AsyncTaskCompat.html). Example: `AsyncTaskCompat.executeParallel(task, params);` – Knossos Apr 05 '16 at 11:33
  • probably the best example to understand the real picture is here: http://android-er.blogspot.in/2014/04/run-multi-asynctask-as-same-time.html – shaby Apr 06 '16 at 18:13
  • @Knossos, thanks for pointing that out, but please bear in mind that in a real world you will most likely want to split tasks in several groups (networking, DB, or whatever criteria) and to run a group on a dedicated thread pool (versus running all tasks in one pool). – Vit Khudenko Apr 06 '16 at 20:53
  • That depends entirely on the use-case, and the asker doesn't specify. I think it is still on-topic. – Knossos Apr 07 '16 at 05:55
  • 1
    @Arhimed Please, update the answer since `AsyncTaskCompat.executeParallel(task, params);` is now [deprecated](https://developer.android.com/reference/android/support/v4/os/AsyncTaskCompat.html) – Kirill Starostin Aug 19 '17 at 14:25
  • 1
    @KirillStarostin Thanks for pointing out, updated. Initially, feeling its redundancy, I did not want to mention it at all. I like they got rid of it. – Vit Khudenko Aug 19 '17 at 19:18
  • When making changes to a question or answer, please don't use "edited" or "updated" tags to flag a change. Instead simply insert the new information where you would have if it had been included originally. We can tell what changes if we need to, and readability of the question or answer is more important than showing chronological updates. – the Tin Man Jan 05 '20 at 19:48
224

This allows for parallel execution on all android versions with API 4+ (Android 1.6+):

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
void startMyTask(AsyncTask asyncTask) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}

This is a summary of Arhimed's excellent answer.

Please make sure you use API level 11 or higher as your project build target. In Eclipse, that is Project > Properties > Android > Project Build Target. This will not break backward compatibility to lower API levels. Don't worry, you will get Lint errors if your accidentally use features introduced later than minSdkVersion. If you really want to use features introduced later than minSdkVersion, you can suppress those errors using annotations, but in that case, you need take care about compatibility yourself. This is exactly what happened in the code snippet above.

Update 2022

Note that AsyncTasks got deprecated quite a while ago. If you use Kotlin, it is recommended to use Kotlin Coroutines and if you depend on using Java in your project, an alternative is to use Executors from java.util.concurrent. (source)

sulai
  • 5,204
  • 2
  • 29
  • 44
  • 3
    Had to change the TimerTask.THREAD_POOL_EXECUTOR to AsynTask.THREAD_POOL_EXECUTOR then this example works – Ronnie Mar 13 '13 at 16:00
  • 2
    To void issues you can make the call generic: `void startMyTask(AsyncTask asyncTask)` (if your doInBackground takes Void) – Peter Mar 28 '13 at 15:17
  • You can simplify the above code even more if your class extends AsyncTask. It worked great sulai, thanks. – Peter Arandorenko Jan 26 '14 at 05:06
  • 1
    Thanks a lot. I had a scenario where I need to execute two async tasks simultaneously to read data from the web server, but I found the server receives only one URL and as long as that particular request is not completed, the second AsyncTask's url will not hit the server. Your answer solved my issue :) – Santhosh Gutta Jun 05 '14 at 20:51
  • @Santhosh Gutta shall I get a sample code which you have done to execute two async tasks simultaneously? – Kailas Bhakade Apr 24 '17 at 11:15
  • @Kailas Bhakade Sorry for the late reply. I dont have that piece of code now, but I have used the above solution to get multiple async tasks run simultaneously. The reason for delayed execution is that the async tasks share the global execution executor when you call execute (...) method but for executeOnExecutor (...), the async tasks run in a pool of executors which is optimized for parallel executions. If the mobile device has multiple cores, then that simultaneous execution is much more vividly seen. – Santhosh Gutta Jun 12 '17 at 20:20
  • 1
    I keep getting upvotes for this, thank you. :) But I've the impression many people try to use AsyncTask for networking where excellent libraries like [Volley](https://developer.android.com/training/volley/index.html), [Glide](http://bumptech.github.io/glide/) or [Swagger](https://swagger.io/) are better choices. Make sure to check these out before using AsyncTask :) – sulai Sep 06 '17 at 17:39
21

Making @sulai suggestion more generic :

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
public static <T> void executeAsyncTask(AsyncTask<T, ?, ?> asyncTask, T... params) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}   
AsafK
  • 2,425
  • 3
  • 32
  • 38
11

Just to include the latest update (UPDATE 4) in @Arhimed 's immaculate answer in the very good summary of @sulai:

void doTheTask(AsyncTask task) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Android 4.4 (API 19) and above
        // Parallel AsyncTasks are possible, with the thread-pool size dependent on device
        // hardware
        task.execute(params);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Android 3.0 to
        // Android 4.3
        // Parallel AsyncTasks are not possible unless using executeOnExecutor
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    } else { // Below Android 3.0
        // Parallel AsyncTasks are possible, with fixed thread-pool size
        task.execute(params);
    }
}
Ali Nem
  • 5,252
  • 1
  • 42
  • 41
4

The android developers example of loading bitmaps efficiently uses a custom asynctask (copied from jellybean) so you can use the executeOnExecutor in apis lower than < 11

http://developer.android.com/training/displaying-bitmaps/index.html

Download the code and go to util package.

OriolJ
  • 2,762
  • 1
  • 28
  • 22
3

It is posible. My android device version is 4.0.4 and android.os.Build.VERSION.SDK_INT is 15

I have 3 spinners

Spinner c_fruit=(Spinner) findViewById(R.id.fruits);
Spinner c_vegetable=(Spinner) findViewById(R.id.vegetables);
Spinner c_beverage=(Spinner) findViewById(R.id.beverages);

And also I have a Async-Tack class.

Here is my spinner loading code

RequestSend reqs_fruit = new RequestSend(this);
reqs_fruit.where="Get_fruit_List";
reqs_fruit.title="Loading fruit";
reqs_fruit.execute();

RequestSend reqs_vegetable = new RequestSend(this);
reqs_vegetable.where="Get_vegetable_List";
reqs_vegetable.title="Loading vegetable";
reqs_vegetable.execute();

RequestSend reqs_beverage = new RequestSend(this);
reqs_beverage.where="Get_beverage_List";
reqs_beverage.title="Loading beverage";
reqs_beverage.execute();

This is working perfectly. One by one my spinners loaded. I didn't user executeOnExecutor.

Here is my Async-task class

public class RequestSend  extends AsyncTask<String, String, String > {

    private ProgressDialog dialog = null;
    public Spinner spin;
    public String where;
    public String title;
    Context con;
    Activity activity;      
    String[] items;

    public RequestSend(Context activityContext) {
        con = activityContext;
        dialog = new ProgressDialog(activityContext);
        this.activity = activityContext;
    }

    @Override
    protected void onPostExecute(String result) {
        try {
            ArrayAdapter<String> adapter = new ArrayAdapter<String> (activity, android.R.layout.simple_spinner_item, items);       
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            spin.setAdapter(adapter);
        } catch (NullPointerException e) {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        } catch (Exception e)  {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
        super.onPostExecute(result);

        if (dialog != null)
            dialog.dismiss();   
    }

    protected void onPreExecute() {
        super.onPreExecute();
        dialog.setTitle(title);
        dialog.setMessage("Wait...");
        dialog.setCancelable(false); 
        dialog.show();
    }

    @Override
    protected String doInBackground(String... Strings) {
        try {
            Send_Request();
            } catch (NullPointerException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        return null;
    }

    public void Send_Request() throws JSONException {

        try {
            String DataSendingTo = "http://www.example.com/AppRequest/" + where;
            //HttpClient
            HttpClient httpClient = new DefaultHttpClient();
            //Post header
            HttpPost httpPost = new HttpPost(DataSendingTo);
            //Adding data
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);

            nameValuePairs.add(new BasicNameValuePair("authorized","001"));

            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            // execute HTTP post request
            HttpResponse response = httpClient.execute(httpPost);

            BufferedReader reader;
            try {
                reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                StringBuilder builder = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    builder.append(line) ;
                }

                JSONTokener tokener = new JSONTokener(builder.toString());
                JSONArray finalResult = new JSONArray(tokener);
                items = new String[finalResult.length()]; 
                // looping through All details and store in public String array
                for(int i = 0; i < finalResult.length(); i++) {
                    JSONObject c = finalResult.getJSONObject(i);
                    items[i]=c.getString("data_name");
                }

            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
SilentKiller
  • 6,944
  • 6
  • 40
  • 75
Sajitha Rathnayake
  • 1,688
  • 3
  • 26
  • 47
  • 2
    how do you even see that they're not executed sequentially? – behelit Jan 12 '18 at 02:51
  • @behelit Does it matter when it is working? Please read Accepted answer for more details https://stackoverflow.com/a/4072832/2345900 – Sajitha Rathnayake Jan 12 '18 at 05:33
  • 2
    Sorry I don't understand. If you can't tell whether your asyncs are sequential or truly async then it does matter. Also, the linked answer says to use the executeonexecutor method, which you are not actually using in your answer. Sorry if I missed something. – behelit Jan 14 '18 at 00:03
  • 1
    **_"One by one my spinners loaded."_** You literally said it yourself that this is a serial execution, not a parallel execution. Each one of our AsyncTasks are added to a queue and processed sequentially. This does not answer the OP's question. – Joshua Pinter Jun 06 '18 at 16:48
  • This is a very old answer. If this is not work for you please refer accepted answer – Sajitha Rathnayake Jun 07 '18 at 02:34
3

if you want to execute tasks parallel,you need call the method executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name") after Android version of 3.0; but this method is not exist before Android 3.0 and after 1.6 because it execute parallel by itself, So I suggest you customize your own AsyncTask class in your project,to avoid throw exception in different Android version.

alpha
  • 71
  • 7