Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASyncTasks blocking others

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);
    }

}
like image 328
Addev Avatar asked Aug 28 '12 22:08

Addev


3 Answers

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)

like image 97
Marcin Orlowski Avatar answered Oct 13 '22 20:10

Marcin Orlowski


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.

like image 28
Nikolay Elenkov Avatar answered Oct 13 '22 21:10

Nikolay Elenkov


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.

like image 44
type-a1pha Avatar answered Oct 13 '22 19:10

type-a1pha