Запуск нескольких 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 будет производить вывод.

Я что-то пропустил очевидно, здесь и/или делать что-то глупое? Разве невозможно одновременно запустить две AsyncTasks?

Edit: я понял, что телефон, о котором идет речь, работает Android 1.5, я обновил описание проблемы. соответственно. У меня нет этой проблемы с HTC Hero под управлением Android 2.1. Хммм ...

7 ответов


AsyncTask использует шаблон пула потоков для запуска материала из doInBackground(). Проблема изначально (в ранних версиях ОС Android) размер пула был всего 1, Что означает отсутствие параллельных вычислений для группы AsyncTasks. Но позже они исправили это, и теперь размер составляет 5, поэтому не более 5 AsyncTasks могут работать одновременно. К сожалению, я не помню, в какой именно версии они это изменили.

обновление:

вот какой ток (2012-01-27) API говорит об этом:

при первом введении AsyncTasks выполнялись последовательно в одном фоновом потоке. Начиная с DONUT, это было изменено на пул потоков, позволяющий нескольким задачам работать параллельно. После HONEYCOMB планируется изменить это обратно на один поток, чтобы избежать распространенных ошибок приложения, вызванных параллельным выполнением. Если вы действительно хотите параллельного выполнения, вы можете использовать executeOnExecutor (Executor, Params...) версия этот метод с THREAD_POOL_EXECUTOR; однако см. комментарий там для предупреждений о его использовании.

пончик-Android 1.6, HONEYCOMB-Android 3.0.

обновление: 2

комментарий kabuko С Mar 7 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 позволяет использовать пользовательский пул потоков executor через 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 ведет себя иначе, чем было описано в обновление: 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 элементов и уменьшает максимальное количество потоков до количества ядер процессора * 2 + 1. Приложения по-прежнему могут отправлять одинаковое количество задач.


Это позволяет для параллельного выполнения на всех версиях Android с API 4+ (Андроид 1.6+):

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

это резюме превосходного ответа Архимеда.

пожалуйста, убедитесь, что вы используете уровень API 11 или выше в качестве цели сборки проекта. В Eclipse это Project > Properties > Android > Project Build Target. это не сломать обратную совместимость до более низких уровней API. не волнуйтесь, вы получите Линт ошибки, если случайно использовать функции, введенные позже, чем minSdkVersion. Если вы действительно хотите использовать функции, появившиеся позднее minSdkVersion, вы можете подавить эти ошибки с помощью аннотаций, но в этом случае, необходимо позаботиться о совместимости сами. Именно это и произошло в приведенном выше фрагменте кода.


сделать предложение @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);
}   

просто включить последнее обновление (Обновление 4) в безупречный ответ @Arhimed в очень хорошем резюме @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);
    }
}

пример Android-разработчиков загрузки растровых изображений эффективно использует пользовательскую asynctask (скопированную из jellybean), поэтому вы можете использовать executeOnExecutor в API ниже

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

загрузите код и перейдите в пакет util.


- это возможно. Моя версия android-устройства-4.0.4 и android.ОС.Строить.ВЕРСИЯ.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.

вот мой код загрузки spinner

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

Если вы хотите выполнять задачи параллельно,необходимо вызвать метод executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name") после Android версии 3.0; но этот метод не существует до Android 3.0 и после 1.6, потому что он выполняется параллельно сам по себе,поэтому я предлагаю вам настроить свой собственный класс AsyncTask в вашем проекте, чтобы избежать исключения броска в другой версии Android.