Originally published at
Moishe Beshkin. You can comment here or
there.
Number of simultaneous tasks in Android app limited by default to number of processors and cores in a device.
Before Android HONEYCOMB (version 3.0) simultaneous tasks were executed without limits to processors. App had to control number or tasks executed at once in order to prevent app from halting.
Starting from Honeycomb, Operating System stated controlling the number of simultaneous tasks. So, the same code started working differently.
We have the following AsyncTask:
public class ExampleTask extends AsyncTask
{
@Override
protected String doInBackground(String... params) {
// ....
// some action
// ....
}
}
Simple execution of this task will lead to different results on older and newer Androids.
for (int i = 0; i < 10; i++){
new ExampleTask().execute();
}
Pre-Honeycomb Android will execute all 10 tasks at once. In most cases this will lead to hang of your phone, as soon as most of devices with pre-3.0 Android have single core processor.
Starting from Honeycomb, OS will set all tasks in a queue and you will see the results one-by-one from each task.
So, I would recommend to do the following:
1. Create two separate tasks.
public class ExampleTaskSym extends AsyncTask
{
@Override
protected String doInBackground(String... params) {
// ....
// some action
// ....
}
}
and
public class ExampleTask extends AsyncTask
{
@Override
protected String doInBackground(String... params) {
// ....
for (int i = 0; i < 10; i++){
// some action
}
// ....
}
}
2. Use the following code to assure, that your code is correctly executed on different versions of Android.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
for (int i = 0; i < 10; i++){
new ExampleTaskSym().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}else{
new ExampleTask().execute();
}
At this moment your app on newer Android will execute 10 simultaneous tasks at once and they will be executed by OS following the number of processor cores.
This will work properly in case you have not many tasks and device has more than one core.
Actually, the executor AsyncTask.THREAD_POOL_EXECUTOR means the following:
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 int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
So, if your device has 4 processor cores, than at once there will be executed 4 tasks and 6 will will be stored in pool. Then on execution processing, tasks from pool will be executed, until all tasks are executed.
The problem will certainly appear, when number of tasks is bшgger, than MAXIMUM_POOL_SIZE.
Most probably, you will get an error as the following:
java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@145b8bdd rejected from java.util.concurrent.ThreadPoolExecutor@19861352[Running, pool size = 17, active threads = 17, queued tasks = 128, completed tasks = 122]
You can override ThreadPoolExecutor with your own.
for (int i = 0; i < 100; i++){
new ExampleTaskSym().executeOnExecutor(new ThreadPoolExecutor(10, // core size
50, // max size
10*60, // idle timeout
TimeUnit.SECONDS,
new ArrayBlockingQueue(20)));
}
I tested with more than 100 tasks for such code and no crashes were detected on any modern devices.