Выполнить 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> {
}