同时运行多个asynctask——不可能?

我正在尝试同时运行两个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就没有这个问题。嗯…

182383 次浏览

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

更新:

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

首次引入时,AsyncTasks是在单个后台线程上串行执行的。从DONUT开始,它被更改为一个线程池,允许多个任务并行操作。在HONEYCOMB之后,计划将其更改为单个线程,以避免并行执行引起的常见应用程序错误。如果你真的想要并行执行,你可以使用这个方法的executeOnExecutor(Executor, Params…)版本THREAD_POOL_EXECUTOR;但是,请参阅注释中关于其使用的警告。

DONUT是Android 1.6, HONEYCOMB是Android 3.0。

更新:2

参见kabukoMar 7 2012 at 1:27的注释。

事实证明,对于使用“允许多个任务并行操作的线程池”的api(从1.6开始到3.0结束),同时运行的AsyncTasks的数量取决于有多少任务已经被传递执行,但尚未完成它们的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有以下字段:

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。应用程序仍然可以提交相同数量的任务。

这允许在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精彩回答的总结。

请确保使用API级别11或更高的级别作为项目构建目标。在Eclipse中,它是Project > Properties > Android > Project Build Target这将not打破向后兼容到较低的API级别。别担心,如果你不小心使用了minSdkVersion之后引入的特性,你会得到Lint错误。如果你真的想使用在minSdkVersion之后引入的特性,你可以使用注释来抑制这些错误,但在这种情况下,你需要注意兼容性你自己。这正是上面代码片段中所发生的情况。

更新2022

注意,AsyncTasks在很长一段时间前得到了弃用。如果你使用Kotlin,建议使用芬兰湾的科特林协同程序,如果你在项目中依赖于使用Java,另一种选择是使用java.util.concurrent中的Executors(源)

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

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

这是可能的。 我的android设备版本是4.0.4和android.os. build . version。SDK_INT是15

我有3个旋转器

Spinner c_fruit=(Spinner) findViewById(R.id.fruits);
Spinner c_vegetable=(Spinner) findViewById(R.id.vegetables);
Spinner c_beverage=(Spinner) findViewById(R.id.beverages);

我还有一个Async-Tack类。

这是我的转轮加载代码

RequestSend reqs_fruit = new RequestSend(this);
reqs_fruit.where="Get_fruit_List";
reqs_fruit.title="Loading fruit";
reqs_fruit.execute();


RequestSend reqs_vegetable = new RequestSend(this);
reqs_vegetable.where="Get_vegetable_List";
reqs_vegetable.title="Loading vegetable";
reqs_vegetable.execute();


RequestSend reqs_beverage = new RequestSend(this);
reqs_beverage.where="Get_beverage_List";
reqs_beverage.title="Loading beverage";
reqs_beverage.execute();

这工作得很完美。我的纺纱机一个接一个装上。我没有使用executeOnExecutor。

这是我的异步任务类

public class RequestSend  extends AsyncTask<String, String, String > {


private ProgressDialog dialog = null;
public Spinner spin;
public String where;
public String title;
Context con;
Activity activity;
String[] items;


public RequestSend(Context activityContext) {
con = activityContext;
dialog = new ProgressDialog(activityContext);
this.activity = activityContext;
}


@Override
protected void onPostExecute(String result) {
try {
ArrayAdapter<String> adapter = new ArrayAdapter<String> (activity, android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spin.setAdapter(adapter);
} catch (NullPointerException e) {
Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
e.printStackTrace();
} catch (Exception e)  {
Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
super.onPostExecute(result);


if (dialog != null)
dialog.dismiss();
}


protected void onPreExecute() {
super.onPreExecute();
dialog.setTitle(title);
dialog.setMessage("Wait...");
dialog.setCancelable(false);
dialog.show();
}


@Override
protected String doInBackground(String... Strings) {
try {
Send_Request();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


public void Send_Request() throws JSONException {


try {
String DataSendingTo = "http://www.example.com/AppRequest/" + where;
//HttpClient
HttpClient httpClient = new DefaultHttpClient();
//Post header
HttpPost httpPost = new HttpPost(DataSendingTo);
//Adding data
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);


nameValuePairs.add(new BasicNameValuePair("authorized","001"));


httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
// execute HTTP post request
HttpResponse response = httpClient.execute(httpPost);


BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuilder builder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line) ;
}


JSONTokener tokener = new JSONTokener(builder.toString());
JSONArray finalResult = new JSONArray(tokener);
items = new String[finalResult.length()];
// looping through All details and store in public String array
for(int i = 0; i < finalResult.length(); i++) {
JSONObject c = finalResult.getJSONObject(i);
items[i]=c.getString("data_name");
}


} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}


} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

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

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