Multiple AsyncTask In Android

What is AsyncTask

AsyncTask is an abstract Android class which helps the Android applications to perform tasks without blocking the UI Thread. AsyncTask is designed to be a helper class around Thread and Handler.

Thread Pool Pattern

AsyncTask uses a thread pool pattern for running the stuff from doInBackground()

The Thread pool Pattern is where number of Threads are created to perform a number of Tasks. It is basically a container where multiple threads come in a queue for different task.

Multiple AsyncTasks In Android

In earlier version of Android (Donut and below), multiple AsyncTasks execution were not allowed. The pool size was just 1. But after Donut (1.6) it has been relaxed and now the size is 5, so at most 5 AsyncTasks can run simultaneously.

Limitation Of AsyncTask

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

Delayed Queue Size

AsyncTasks use a fixed size queue internally for storing delayed tasks. Queue size is 10 by default. If you start 15 AsyncTasks 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 AsyncTasks 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.

Change Delayed Queue Size

Starting from Android version 3.0, the API allows to use your custom thread pool executor via AsyncTask.executeOnExecutor(Executor exec, Params… params) method. This allows to configure the size of the delayed tasks queue (Default size 10) . Here you can give your own executor to execute custom number of thread means you can change the number of thread to execute in parallel. There are some common application errors caused by parallel execution like InterruptedExecution and IllegalStateException so it is recommended to do not use multiple AsyncTask in parallel but, if you really have a need you can override Android’s default behaviour using AsyncTask.executeOnExecutor(Executor, Object[]), passing in a THREAD_POOL_EXECUTOR.     

Criteria of using Multiple AsyncTask

Before Donut (Till 1.6 Version)
There was no concept of Multiple AsyncTask.

After Donut and till Honeycomb (1.6 – 3.0)
Here it uses multiple AsyncTask in parallel by default and you cannot customize it.

After Honeycomb and till Jelly Bean (Version 3.0 – 4.3.1)
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);

From KitKat (4.4 Above):
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);

Example to showcase multiple AsyncTask

public class MultipleAsyncTask extends Activity
{
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		runMultipleAsyncTask(); // Start Async Task
	}
	private void runMultipleAsyncTask() // Run Multiple Async Task
	{
		FirstAsyncTask asyncTask = new FirstAsyncTask(); // First
		if(AppUtil.isCurrentVersionHoneycombAndAbove()) // Above Api Level 13
		{
			asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
		}
		else // Below Api Level 13
		{
			asyncTask.execute();
		}
		SecondAsyncTask asyncTask2 = new SecondAsyncTask(); // Second
		if(AppUtil.isCurrentVersionHoneycombAndAbove())// Above Api Level 13
		{
			asyncTask2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
		}
		else // Below Api Level 13
		{
			asyncTask2.execute();
		}
	}
	//Start First Async Task:
	private class FirstAsyncTask extends AsyncTask<Void, Void, Void>
	{
		@Override
		protected void onPreExecute()
		{
			Log.i("AsyncTask" ,"FirstOnPreExecute()");
		}
		@Override
		protected Void doInBackground(Void... params)
		{
			for(int index = 0; index < 50; index++)
			{
				Log.i("AsyncTask" ,"FirstAsyncTask");
				try
				{
					Thread.sleep(100);
				}
				catch (InterruptedException exception)
				{
					exception.printStackTrace();
				}
			}
			return null;
		}
		@Override
		protected void onPostExecute(Void result)
		{
			Log.d("AsyncTask" ,"FirstonPostExecute()");
		}
	}
	//Start Second Async Task:
	private class SecondAsyncTask extends AsyncTask<Void, Void, Void>
	{
		@Override
		protected void onPreExecute()
		{
			Log.i("AsyncTask" ,"SecondOnPreExecute()");
		}
		@Override
		protected Void doInBackground(Void... params)
		{
			for(int index = 0; index < 50; index++)
			{
				Log.d("AsyncTask" ,"SecondAsyncTask");
				try
				{
					Thread.sleep(100);
				}
				catch (InterruptedException exception)
				{
					exception.printStackTrace();
				}
			}
			return null;
		}
		@Override
		protected void onPostExecute(Void result)
		{
			Log.d("AsyncTask" ,"SecondOnPostExecute()");
		}
	}
}

Result:

FirstOnPreExecute()
SecondOnPreExecute()
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstAsyncTask
SecondAsyncTask
FirstonPostExecute()
SecondOnPostExecute()

Leave a Reply