Запуск нескольких AsyncTasks одновременно - не возможно? - PullRequest
246 голосов
/ 01 ноября 2010

Я пытаюсь запустить две 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 одновременно?

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

Ответы [ 7 ]

425 голосов
/ 01 ноября 2010

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

ОБНОВЛЕНИЕ:

Вот что говорит текущий API (2012-01-27) об этом:

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

DONUT - это 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 позволяет использовать ваш пользовательский пул потоков с помощью метода 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. Приложения могут отправлять одинаковое количество задач.

209 голосов
/ 10 декабря 2012

Это позволяет выполнять параллельное выполнение на всех версиях Android с API 4+ (Android 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. Не беспокойтесь, вы получите ошибки Lint, если случайно воспользуетесь функциями, представленными позже minSdkVersion.Если вы действительно хотите использовать функции, представленные позже, чем minSdkVersion, вы можете устранить эти ошибки с помощью аннотаций, но в этом случае вам нужно позаботиться о совместимости самостоятельно .Именно это и произошло в приведенном выше фрагменте кода.

20 голосов
/ 30 января 2014

Как сделать предложение @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);
}   
7 голосов
/ 27 марта 2017

Просто включить последнее обновление (ОБНОВЛЕНИЕ 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);
    }
}
4 голосов
/ 22 января 2013

Пример загрузки растровых изображений разработчиками Android эффективно использует пользовательскую асинхронную задачу (скопированную с jellybean), поэтому вы можете использовать executeOnExecutor с apis ниже <11 </p>

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

Скачать коди перейдите в пакет утилит.

3 голосов
/ 13 февраля 2014

Это возможно.Версия моего 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();
        }
    }
}
2 голосов
/ 24 марта 2017

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...