Выполните AsyncTask несколько раз - PullRequest
127 голосов
/ 16 июня 2011

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

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

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

Спасибо.

Ответы [ 5 ]

217 голосов
/ 16 июня 2011

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

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

Из документов API AsyncTask:

Правила потоков

Для правильной работы этого класса необходимо соблюдать несколько правил потоков:

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

Причины возникновения и запуска экземпляров ASyncTask довольно подробно описаны в ответе Стива Прентиса. Однако, хотя вы ограничены сколько раз вы выполняете 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 останется в куче во время выполнения.

2 голосов
/ 04 марта 2016

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

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

instance = null;

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

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

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

1 голос
/ 04 августа 2012

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

0 голосов
/ 11 сентября 2015

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

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

// 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> {
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...