我试图同时运行两个AsyncTasks。(平台为Android 1.5, HTC Hero) 但是,只执行第一个。下面是一个简单的片段来描述我的问题:

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

我期望的输出是:

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

等等。然而,我得到的是:

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

第二个AsyncTask永远不会被执行。如果我改变execute()语句的顺序,只有foo任务将产生输出。

我是否遗漏了一些明显的东西,或者做了一些愚蠢的事情?不可能同时运行两个asynctask吗?

编辑:我意识到问题手机运行Android 1.5,我更新了问题描述。相应的行动。运行Android 2.1的HTC Hero就没有这个问题。嗯…


当前回答

android开发者有效加载位图的例子使用了自定义asynctask(从jellybean复制),所以你可以在低于< 11的api中使用executeOnExecutor

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

下载代码并进入util包。

其他回答

让@sulai的建议更通用:

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

如果你想并行执行任务,你需要调用方法executeOnExecutor(AsyncTask. executor)。Android 3.0版本后的THREAD_POOL_EXECUTOR,“你的任务名称”;但是这个方法在Android 3.0之前和1.6之后都不存在,因为它自己并行执行,所以我建议你在你的项目中定制自己的AsyncTask类,以避免在不同的Android版本中抛出异常。

AsyncTask使用线程池模式来运行来自doInBackground()的东西。问题是最初(在早期的Android OS版本中)池大小只有1,这意味着一堆asynctask没有并行计算。但后来他们修复了这个问题,现在大小是5,所以最多5个AsyncTasks可以同时运行。不幸的是,我不记得他们在哪个版本中改变了这一点。

更新:

以下是当前(2012-01-27)API对此的解释:

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是Android 1.6, HONEYCOMB是Android 3.0。

更新:2

请看歌舞伎2012年3月7日1:27的评论。

事实证明,对于使用“允许多个任务并行操作的线程池”的api(从1.6开始到3.0结束),同时运行的AsyncTasks的数量取决于有多少任务已经被传递执行,但还没有完成它们的doInBackground()。

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.

同时运行的任务数量是有限制的。由于AsyncTask使用线程池执行器,工作线程的最大数量有限(128),而延迟任务队列的大小固定为10,如果你试图执行超过138个自定义任务,应用程序将崩溃java.util.concurrent.RejectedExecutionException。

从3.0开始,API允许通过AsyncTask使用自定义线程池执行器。executeOnExecutor(Executor exec, Params…params)方法。例如,如果默认的10不是您需要的,这允许配置延迟任务队列的大小。

正如@Knossos提到的,有一个选项可以使用AsyncTaskCompat。executeParallel(任务,params);从支持v.4库并行运行任务,而无需担心API级别。此方法在API级别26.0.0中已弃用。

更新:3

这里有一个简单的测试应用程序来玩任务的数量,串行和并行执行:https://github.com/vitkhudenko/test_asynctask

更新:4(感谢@penkzhou指出这一点)

从Android 4.4开始,AsyncTask的行为与UPDATE: 2部分所描述的不同。有一个修复,以防止AsyncTask创建太多的线程。

在Android 4.4 (API 19)之前,AsyncTask有以下字段:

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

在Android 4.4 (API 19)中,上述字段更改为:

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

此更改将队列的大小增加到128个项,并将最大线程数减少到CPU内核数* 2 + 1。应用程序仍然可以提交相同数量的任务。

为了在@Arhimed完美的回答中包括最新的更新(更新4),在@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);
    }
}

这允许在API 4+ (android 1.6+)的所有android版本上并行执行:

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

这是对Arhimed精彩回答的总结。

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.

更新2022

请注意,AsyncTasks在很久以前就被弃用了。如果你使用Kotlin,建议使用Kotlin协程,如果你的项目依赖于使用Java,另一种选择是使用Java .util.concurrent中的executor。(源)