同时运行多个AsyncTasks-不可能吗?


259

我正在尝试同时运行两个AsyncTask。(平台是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任务会产生输出。

我在这里缺少明显的东西和/或做一些愚蠢的事情吗?不能同时运行两个AsyncTasks吗?

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


您的代码对我有用,因此问题必须出在其他地方。您是否在LogCat视图中输入了过滤器?;-)
mreichelt 2010年

嗯,很奇怪。我在logcat中没有任何过滤。您是否也在使用1.6?如果是这样,哪个手机?
10年

糟糕,刚意识到它正在运行(古老的)Android 1.5
狂热2010年

1
我使用Android 1.6作为目标,并使用Android 2.1模拟器。因此,如果仅在搭载Android 1.5的HTC Hero上确实发生了问题-将它们拧紧,就可以了。;-) HTC Hero已将其更新为更新的Android版本。如果有一些制造商把事情搞砸了,我不会为之烦恼。此外,我不再关心Android 1.5。
mreichelt

AsyncTask应该用于5 ms的较短任务。转到ThreadPoolExecutor(developer.android.com/reference/java/util/concurrent/…)。相关岗位:stackoverflow.com/questions/6964011/...
拉温德拉巴布

Answers:


438

AsyncTask使用线程池模式来运行doInBackground()中的内容。问题最初是(在早期的Android OS版本中)池大小仅为1,这意味着一堆AsyncTasks没有并行计算。但是后来他们解决了这个问题,现在大小为5,因此最多可以同时运行5个AsyncTask。不幸的是,我不记得他们确切地更改了哪个版本。

更新:

以下是当前(2012-01-27)AP​​I对此的说明:

首次引入时,AsyncTasks在单个后台线程上串行执行。从DONUT开始,它已更改为线程池,允许多个任务并行运行。在HONEYCOMB之后,计划将其更改回单个线程,以避免并行执行引起的常见应用程序错误。如果您确实想要并行执行,则可以将此方法的executeOnExecutor(Executor,Params ...)版本与THREAD_POOL_EXECUTOR一起使用;但是,请参阅此处的注释以获取有关其使用的警告。

DONUT是Android 1.6,HONEYCOMB是Android 3.0。

更新:2

请参阅kabuko来自的评论Mar 7 2012 at 1:27

事实证明,对于使用“允许多个任务并行运行的线程池”(从1.6开始到3.0结束)的API,同时运行的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所提到的,AsyncTaskCompat.executeParallel(task, 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。应用程序仍可以提交相同数量的任务。


11
仅供参考,核心池大小为5,但最大为128。这意味着,如果您填充队列,则可以同时运行5个以上(最多128个)。
kabuko 2012年

2
@kabuko:你绝对正确。刚刚在2.2上进行了调查,并确认如果5个任务已在运行(核心池大小为5),则它将开始将其余任务放入延迟任务的队列中,但是如果队列已满(最大队列大小为10) ),然后启动该任务的其他辅助线程,因此该任务立即启动。
维特·胡登科

1
刚刚拯救了我的一天,字幕英雄:)谢谢
Karoly

2
请更新此答案以获取新的兼容功能。示例:AsyncTaskCompat.executeParallel(task, params);
Knossos

1
@Arhimed,请更新答案,因为AsyncTaskCompat.executeParallel(task, params);现在已弃用
Kirill Starostin

218

这允许在具有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不会破坏对较低API级别的向后兼容性。不用担心,如果您不慎使用迟于引入的功能,则会出现Lint错误minSdkVersion。如果你真的想使用的功能迟介绍minSdkVersion,您可以使用注释抑制这些错误,但在这种情况下,你需要照顾兼容性自己。这正是上面的代码片段中发生的事情。


3
如果将TimerTask.THREAD_POOL_EXECUTOR更改为AsynTask.THREAD_POOL_EXECUTOR,则此示例有效
Ronnie

2
要使问题无效,可以使该呼叫通用:( void startMyTask(AsyncTask<Void, ?, ?> asyncTask) 如果您的doInBackground无效)
彼得·彼得

如果您的类扩展了AsyncTask <Void,Integer,Void>,则可以进一步简化上述代码。苏莱工作得很好,谢谢。
Peter Arandorenko 2014年

1
非常感谢。我有一个场景,我需要同时执行两个异步任务以从Web服务器读取数据,但是我发现服务器仅接收一个URL,并且只要该特定请求未完成,第二个AsyncTask的URL就不会到达服务器。您的答案解决了我的问题:)
Santhosh Gutta 2014年

1
我一直对此表示支持,谢谢。:)但给人的印象是,很多人尝试使用AsyncTask进行网络连接,而VolleyGlideSwagger等优秀的库是更好的选择。使用AsyncTask之前,请务必检查这些内容:)
sulai

21

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

更通用的解决方案:AsyncTaskCompat.executeParallel(new MyAsyncTask,myprams);
维卡什·库玛·维尔玛

11

只需在@sulai的非常好的摘要中将@Arhimed的完美答案中包含最新更新(UPDATE 4):

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

哇,真棒。这意味着.executeOnExecutor()API> = KITKAT现在是多余的,是吗?
Taslim Oseni


3

这是可能的。我的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();

这很完美。我的微调器一一加载。我没有使用executeExecuteExecutor。

这是我的异步任务类

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

2
您如何看待它们没有按顺序执行?
相信

@behelit工作时有关系吗?请阅读接受的答案以获取更多详细信息stackoverflow.com/a/4072832/2345900
Sajitha Rathnayake,

2
对不起,我听不懂。如果您无法确定异步是顺序异步还是真正异步,那么这很重要。同样,链接的答案表示要使用executeonexecutor方法,您实际上并未在答案中使用该方法。对不起,如果我错过了什么。
相信

1
“我的微调器一一装载。” 您从字面上说是一个串行执行,而不是并行执行。我们的每个AsyncTasks都添加到队列中并按顺序处理。这不能回答OP的问题。
Joshua Pinter18年

这是一个非常古老的答案。如果这对您不起作用,请参阅公认的答案
Sajitha Rathnayake,2018年

3

如果要并行执行任务,则需要executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")在Android 3.0以后的版本中调用方法;但是此方法在Android 3.0之前和1.6之后并不存在,因为它本身是并行执行的,因此建议您在项目中自定义自己的AsyncTask类,以避免在其他Android版本中引发异常。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.