Одновременное использование нескольких 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?

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

Ответ 1

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

UPDATE:

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 5

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

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

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

Ответ 6

Это возможно. Моя версия устройства для 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.

Вот мой код загрузки 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();

Это работает отлично. Один за другим загружались мои прядильщики. Я не выполнял функциюОнЭкрансер.

Вот мой класс задач Async

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

Ответ 7

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

Ответ 8

Replace this 'asyncTask.execute();
asyncTask2.execute();
asyncTask3.execute();
asyncTask4.execute();
asyncTask5.execute();'

with this 



asyncTask1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
  asyncTask2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
  asyncTask3.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
  asyncTask4.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
  asyncTask5.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

it works after android honeycomb.

[Read More Here][1]


  [1]: https://www.shaikhutech.com/2018/12/biggest-problem-when-it-comes-to-run.html