32

I've 2 ASyncTasks, one retrieves a value from an httpPost and the other update some elements of the UI (including an listview). The problem is that since both ASyncTasks share the same background thread, if the network opperation start first and runs slow (due a bad network connectivity). The others background thread takes too much time making the app irresponsible.

Since both ASyncTasks are independient is pretty stupid one to make wait the other. It would be more logical asynctasks different classes use different threads, am I wrong?

Reading the ASyncTask doc. Talks about using executeOnExecutor(), but how can I solve that in a API level lower than 11?

Here goes a small example that reproduces the "problem"

        new Task1().execute();
        new Task2().execute();

With

public class Task1 extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {
        GLog.e("doInBackground start 1");
        SystemClock.sleep(9000);
        GLog.e("doInBackground end 1");
        return null;
    }

    @Override
    protected void onPreExecute() {
        GLog.e("onPreExecute 1");
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Void result) {
        GLog.e("onPostExecute 1");
        super.onPostExecute(result);
    }

}

public class Task2 extends AsyncTask<Void, Void, Void> {

    @Override
    protected void onPreExecute() {
        GLog.e("onPreExecute 2");
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... params) {
        GLog.e("doInBackground start 2");
        SystemClock.sleep(9000);
        GLog.e("doInBackground end 2");
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        GLog.e("onPostExecute 2");
        super.onPostExecute(result);
    }

}
Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
Addev
  • 31,819
  • 51
  • 183
  • 302
  • Why do you have an AsyncTask to update the UI? You need to do that synchronously on the Ii thread. Does your UI AsyncTask do something else on the background thread? – Nicholas Albion Aug 28 '12 at 23:06
  • I use it for example for showing a listview with the news. In the onPreExecute I show the loading() UI, in background retrieve the news from the Internet and in the onPostExecute assign the adapter to the listview. Is that approach incorrect? – Addev Aug 28 '12 at 23:12
  • @Addev : Why don't you just start the second `AsyncTask` in the `onPostExecute(...)` method of the first? If what you need is that one `AsyncTask` relies on the result of the other then you effectively have a synchronous requirement and you shouldn't be running two asynchronous operations in parallel. – Squonk Aug 28 '12 at 23:19
  • Sorry, your post confused me. You said that you use one AsyncTask to do the HTTP post, and the other to update the UI. Now you say that both AsyncTasks access the internet - that makes more sense – Nicholas Albion Aug 28 '12 at 23:20
  • No, I'm using an AsyncTask for (display loading, make httppost, hide loading) in (preexecute, background , postexecute) and other for (nothing, read from database , updateUI). The problem is that the first task blocks the background thread of the second until it ends. – Addev Aug 29 '12 at 06:14

3 Answers3

52

This is how I handle this in my code:

if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
    new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
    new MyAsyncTask().execute();
}

And replace MyAsyncTask with yours Task1 and Task2 respectively. Basically change in AsyncTask appeared in Honeycomb (see Android SDK docs here in "Order of execution" section), so before that, you launch it as usual, for HC and up, use executeOnExecutor() if you do not like new behaviour (noone does, I think)

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
8

A slightly more general way to do this is to put two helper methods in a utility class like so:

class Utils {

    @SuppressLint("NewApi")
    public static <P, T extends AsyncTask<P, ?, ?>> void execute(T task, P... params) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
        } else {
            task.execute(params);
        }
    }
}

Then you can execute tasks with Utils.execute(mytask) or Utils.execute(mytask, params) and it will take care of executing them in parallel.

Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
  • Thanks. Note for posterity, don't bother trying to override `AsyncTask.execute()` -- you can't; it's `final`. – TalkLittle Jul 13 '13 at 04:27
  • Also the first method is unnecessary and might actually cause unexpected behavior (`params` can be `null` instead of an empty array). Removing it compiles/runs fine on pre-Honeycomb devices. – TalkLittle Jul 18 '13 at 21:02
  • It's not unnecessary, it saves typing :) – Nikolay Elenkov Jul 19 '13 at 01:01
  • It doesn't save typing though, after removing the first method you can still write Utils.execute(task) and then params will just be an empty array. That's how Java varargs work. – TalkLittle Jul 19 '13 at 03:19
  • Hm, I thought I tried that and it didn't work, but that was a long time ago. – Nikolay Elenkov Jul 19 '13 at 03:33
  • I am fairly sure I added it for a reason, but that might have been some local bug. You are right, it's not needed. Removed, thanks! – Nikolay Elenkov Jul 19 '13 at 03:47
1

The problem is that every AsyncTask run in the same ThreadPoolExecutor which is coded into the API. This ThreadPoolExecutor can create a different number of WorkerThread depending on your Android version. I don't remember the number versions but the idea is that in older Android versions it was 1 WorkerThread. Then it was updated to 5 in later versions. And recently got moved back to 1 again. This is why your AsyncTasks are blocked. They run all on the same WorkerThread and thus they execute sequentially. Try using executeOnExecutor with THREAD_POOL_EXECUTOR to achieve true parallel execution. However you can use this only since API 11.

type-a1pha
  • 1,891
  • 13
  • 19