同时运行多个 AsyncTasks —— 不可能吗?
我正在尝试同时运行两个 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 上没有这个问题。嗯...
I'm trying to run two AsyncTasks at the same time. (Platform is Android 1.5, HTC Hero.)
However, only the first gets executed. Here's a simple snippet to describe my problem:
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.");
}
}
The output I expect is:
onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo
And so on. However, what I get is:
onCreate() is done.
bar bar bar
bar bar bar
bar bar bar
The second AsyncTask never gets executed. If I change the order of the execute() statements, only the foo task will produce output.
Am I missing something obvious here and/or doing something stupid? Is it not possible to run two AsyncTasks at the same time?
Edit: I realized the phone in question runs Android 1.5, I updated the problem descr. accordingly. I don't have this problem with an HTC Hero running Android 2.1. Hmmm ...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
AsyncTask 使用线程池模式来运行 doInBackground() 中的内容。问题最初是(在早期的 Android 操作系统版本中)池大小仅为 1,这意味着一堆 AsyncTasks 没有并行计算。但后来他们修复了这个问题,现在大小为 5,因此最多可以同时运行 5 个 AsyncTask。不幸的是我不记得他们到底在哪个版本中改变了这一点。
更新:
以下是当前 (2012-01-27) API 对此的说明:
DONUT是Android 1.6,HONEYCOMB是Android 3.0。
更新:2
请参阅
kabuko
在2012 年 3 月 7 日 1:27
发表的评论。事实证明,对于使用“允许多个任务并行操作的线程池”的 API(从 1.6 开始到 3.0 结束),同时运行的 AsyncTask 的数量取决于已经传递执行的任务数量,但是还没有完成他们的
doInBackground()
。这是我在 2.2 上测试/确认的。假设您有一个自定义 AsyncTask,它仅在
doInBackground()
中休眠一秒钟。 AsyncTasks 在内部使用固定大小的队列来存储延迟任务。队列大小默认为 10。如果您连续启动 15 个自定义任务,则前 5 个任务将进入其doInBackground()
,但其余任务将在队列中等待空闲工作线程。一旦前 5 个任务完成并释放工作线程,队列中的任务就会开始执行。因此在这种情况下最多会同时运行 5 个任务。但是,如果您连续启动 16 个自定义任务,则前 5 个将进入其doInBackground()
,其余 10 个将进入队列,但对于第 16 个,将创建一个新的工作线程,因此它会立即开始执行。所以在这种情况下最多 6 个任务会同时运行。可以同时运行的任务数量是有限制的。由于
AsyncTask
使用线程池执行器,其最大工作线程数有限 (128),并且延迟任务队列的大小固定为 10,因此如果您尝试执行超过 138 个自定义任务,应用程序将崩溃java.util.concurrent.RejectedExecutionException
。从 3.0 开始,API 允许通过 AsyncTask.executeOnExecutor(Executor exec, Params... params) 方法使用自定义线程池执行器。例如,如果您不需要默认值 10,则可以配置延迟任务队列的大小。
正如 @Knossos 提到的,可以选择使用支持 v.4 库中的
AsyncTaskCompat.executeParallel(task, params);
来并行运行任务,而无需考虑 API 级别。此方法在 API 级别 26.0.0 中已弃用。更新:3
这是一个简单的测试应用程序,用于处理任务数量、串行与并行执行:https://github.com/vitkhudenko/test_asynctask
更新:4(感谢 @penkzhou 指出这一点)
从 Android 4.4 开始
AsyncTask
的行为有所不同来自更新:2部分中的描述。 修复可防止AsyncTask
创建太多线程。在 Android 4.4 (API 19) 之前,
AsyncTask
具有以下字段:在 Android 4.4 (API 19) 中,上述字段更改为:
此更改将队列的大小增加到 128 个项目,并减少了最大值线程数 = CPU 核心数 * 2 + 1。应用程序仍然可以提交相同数量的任务。
AsyncTask uses a thread pool pattern for running the stuff from doInBackground(). The issue is initially (in early Android OS versions) the pool size was just 1, meaning no parallel computations for a bunch of AsyncTasks. But later they fixed that and now the size is 5, so at most 5 AsyncTasks can run simultaneously. Unfortunately I don't remember in what version exactly they changed that.
UPDATE:
Here is what current (2012-01-27) API says on this:
DONUT is Android 1.6, HONEYCOMB is Android 3.0.
UPDATE: 2
See the comment by
kabuko
fromMar 7 2012 at 1:27
.It turns out that for APIs where "a pool of threads allowing multiple tasks to operate in parallel" is used (starting from 1.6 and ending on 3.0) the number of simultaneously running AsyncTasks depends on how many tasks have been passed for execution already, but have not finished their
doInBackground()
yet.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 theirdoInBackground()
, 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 theirdoInBackground()
, 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.There is a limit of how many tasks can be run simultaneously. Since
AsyncTask
uses a thread pool executor with limited max number of worker threads (128) and the delayed tasks queue has fixed size 10, if you try to execute more than 138 your custom tasks the app will crash withjava.util.concurrent.RejectedExecutionException
.Starting from 3.0 the API allows to use your custom thread pool executor via
AsyncTask.executeOnExecutor(Executor exec, Params... params)
method. This allows, for instance, to configure the size of the delayed tasks queue if default 10 is not what you need.As @Knossos mentions, there is an option to use
AsyncTaskCompat.executeParallel(task, params);
from support v.4 library to run tasks in parallel without bothering with API level. This method became deprecated in API level 26.0.0.UPDATE: 3
Here is a simple test app to play with number of tasks, serial vs. parallel execution: https://github.com/vitkhudenko/test_asynctask
UPDATE: 4 (thanks @penkzhou for pointing this out)
Starting from Android 4.4
AsyncTask
behaves differently from what was described in UPDATE: 2 section. There is a fix to preventAsyncTask
from creating too many threads.Before Android 4.4 (API 19)
AsyncTask
had the following fields:In Android 4.4 (API 19) the above fields are changed to this:
This change increases the size of the queue to 128 items and reduces the maximum number of threads to the number of CPU cores * 2 + 1. Apps can still submit the same number of tasks.
这允许在具有 API 4+ (Android 1.6+) 的所有 Android 版本上并行执行:
这是 Arhimed 优秀答案的摘要。
请确保您使用 API 级别 11 或更高版本作为您的项目构建目标。在 Eclipse 中,即
Project >属性>安卓>项目构建目标
。 这不会破坏对较低 API 级别的向后兼容性。不用担心,如果您不小心使用了minSdkVersion
。如果您确实想使用minSdkVersion
之后引入的功能,您可以使用注释来抑制这些错误,但在这种情况下,您需要自己注意兼容性。这正是上面代码片段中发生的情况。2022 更新
请注意,
AsyncTasks
很久以前就被弃用了。如果您使用 Kotlin,建议使用 Kotlin Coroutines,如果您依赖于在项目中使用 Java,则另一种选择是使用java.util 中的
。 (来源)Executors
.并发This allows for parallel execution on all android versions with API 4+ (Android 1.6+):
This is a summary of Arhimed's excellent answer.
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 thanminSdkVersion
. If you really want to use features introduced later thanminSdkVersion
, 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.Update 2022
Note that
AsyncTasks
got deprecated quite a while ago. If you use Kotlin, it is recommended to use Kotlin Coroutines and if you depend on using Java in your project, an alternative is to useExecutors
fromjava.util.concurrent
. (source)让@sulai的建议更通用:
Making @sulai suggestion more generic :
只是将最新更新(更新 4)包含在 @Arhimed 的完美答案中,在 @sulai 的非常好的总结中:
Just to include the latest update (UPDATE 4) in @Arhimed 's immaculate answer in the very good summary of @sulai:
Android 开发人员高效加载位图的示例使用自定义异步任务(从 jellybean 复制),因此您可以在低于 << 的 api 中使用executeOnExecutor。 11
http://developer.android.com/training/displaying-bitmaps/index.html html
下载代码并进入util包。
The android developers example of loading bitmaps efficiently uses a custom asynctask (copied from jellybean) so you can use the executeOnExecutor in apis lower than < 11
http://developer.android.com/training/displaying-bitmaps/index.html
Download the code and go to util package.
这是有可能的。
我的 android 设备版本是 4.0.4,android.os.Build.VERSION.SDK_INT 是 15
我有 3 个微调器
,而且我还有一个 Async-Tack 类。
这是我的微调器加载代码
,工作正常。我的旋转器一一装好。我没有使用executeOnExecutor。
这是我的异步任务类
It is posible.
My android device version is 4.0.4 and android.os.Build.VERSION.SDK_INT is 15
I have 3 spinners
And also I have a Async-Tack class.
Here is my spinner loading code
This is working perfectly. One by one my spinners loaded. I didn't user executeOnExecutor.
Here is my Async-task class
如果要并行执行任务,Android 3.0版本之后需要调用方法
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")
;但是这个方法在Android 3.0之前和1.6之后不存在,因为它自己并行执行,所以我建议你在项目中自定义自己的AsyncTask类,以避免在不同的Android版本中抛出异常。if you want to execute tasks parallel,you need call the method
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")
after Android version of 3.0; but this method is not exist before Android 3.0 and after 1.6 because it execute parallel by itself, So I suggest you customize your own AsyncTask class in your project,to avoid throw exception in different Android version.