Выполнить AsyncTask несколько раз

В моей работе я использую класс, который простирается от AsyncTask и параметр, являющийся экземпляром этой AsyncTask. Когда я звоню mInstanceOfAT.execute(""), все в порядке. Но приложение падает, когда я нажимаю кнопку обновления, которая снова вызывает AsyncTask (если сетевое задание не работает). В результате возникает исключение, в котором говорится

Невозможно выполнить задачу: задача уже выполнено (задача может быть выполняется только один раз)

Я попытался вызвать cancel (true) для экземпляра Asyctask, но он тоже не работает. Единственное решение - создать новые экземпляры Asyntask. Правильно ли это?

Спасибо.

Ответ 1

AsyncTask экземпляры могут использоваться только один раз.

Вместо этого просто вызовите свою задачу как new MyAsyncTask().execute("");

В документах API AsyncTask:

Правила Threading

Существует несколько правил потоковой передачи, которые должны соблюдаться для правильного функционирования этого класса:

  • Экземпляр задачи должен быть создан в потоке пользовательского интерфейса.
  • execute (Params...) должен быть вызван в потоке пользовательского интерфейса.
  • Не вызывайте onPreExecute(), onPostExecute (Result), doInBackground (Params...), onProgressUpdate (Прогресс...) вручную.
  • Задача может быть выполнена только один раз (исключение будет выбрано при попытке выполнить второе выполнение.)

Ответ 2

Причины для экземпляров ASyncTask для Fire-and-forget подробно описаны в ответе Стив Прентис. Однако, хотя вы ограничены тем, сколько раз вы выполняете ASyncTask, вы можете делать то, что вам нравится, пока поток работает...

Поместите исполняемый код внутри цикла в doInBackground() и используйте параллельную блокировку для запуска каждого выполнения. Вы можете получить результаты с помощью publishProgress()/onProgressUpdate().

Пример:

class GetDataFromServerTask extends AsyncTask<Input, Result, Void> {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition tryAgain = lock.newCondition();
    private volatile boolean finished = false;

    @Override
    protected Void doInBackground(Input... params) {

        lock.lockInterruptibly();

        do { 
            // This is the bulk of our task, request the data, and put in "result"
            Result result = ....

            // Return it to the activity thread using publishProgress()
            publishProgress(result);

            // At the end, we acquire a lock that will delay
            // the next execution until runAgain() is called..
            tryAgain.await();

        } while(!finished);

        lock.unlock();
    }

    @Override
    protected void onProgressUpdate(Result... result) 
    {
        // Treat this like onPostExecute(), do something with result

        // This is an example...
        if (result != whatWeWant && userWantsToTryAgain()) {
            runAgain();
        }
    }

    public void runAgain() {
        // Call this to request data from the server again
        tryAgain.signal();
    }

    public void terminateTask() {
        // The task will only finish when we call this method
        finished = true;
        lock.unlock();
    }

    @Override
    protected void onCancelled() {
        // Make sure we clean up if the task is killed
        terminateTask();
    }
}

Конечно, это немного сложнее, чем традиционное использование ASyncTask, и вы отказываетесь от использования publishProgress() для фактического отчета о ходе выполнения. Но если память беспокоит вас, тогда этот подход обеспечит сохранение только одной ASyncTask в куче во время выполнения.

Ответ 3

Я поставил статические задачи вращения, которые затем помогли мне прикрепить, отсоединить и снова подключить их к потокам пользовательского интерфейса при изменениях вращения. Но чтобы вернуться к вашему вопросу, я создаю флаг, чтобы увидеть, работает ли поток. Когда вы хотите перезапустить поток, я проверяю, запущена ли задача вращения, если я предупреждаю об этом. Если это не так, я делаю его нулевым, а затем создаю новый, который будет работать вокруг ошибки, которую вы видите. Кроме того, после успешного завершения я удаляю завершенную задачу, связанную с ротацией, чтобы она была готова снова вернуться.

Ответ 4

У меня была такая же проблема. В моем случае у меня есть задача, которую я хочу сделать в onCreate() и в onResume(). Поэтому я сделал статическую статическую Asynctask и получил экземпляр от него. Теперь у нас все та же проблема.

Так что я сделал в onPostExecute():

instance = null;

Помня, что я проверяю статический метод getInstance, что мой экземпляр не является нулевым, иначе я его создаю:

if (instance == null){
    instance = new Task();
}
return instance;

Метод в postExecute очистит экземпляр и заново создаст его. Конечно, это можно сделать вне класса.

Ответ 5

Да, это правда, в документе говорится, что только одна асинтеза может быть выполнена.

Каждый раз, когда вам нужно его использовать, вы должны указать:

// Any time if you need to call her
final FirmwareDownload fDownload = new FirmwareDownload();
fDownload.execute("your parameter");

static class FirmwareDownload extends AsyncTask<String, String, String> {
}