Виконати AsyncTask кілька разів


127

У своїй діяльності я використовую клас, який поширюється на AsyncTask, і параметр, який є примірником цього AsyncTask. Коли я телефоную, mInstanceOfAT.execute("")все нормально. Але програма виходить з ладу, коли я натискаю кнопку оновлення, яка знову викликає AsyncTask (у випадку, якщо мережеве завдання не працює). Після цього з'являється виняток, який говорить

Неможливо виконати завдання: завдання вже виконано (завдання можна виконати лише один раз)

Я спробував виклик скасувати (правда) для екземпляра Asyctask, але він також не працює. Єдине рішення поки що - це створити нові екземпляри Асинтаска. Це правильний шлях?

Дякую.

Відповіді:


217

AsyncTask екземпляри можна використовувати лише один раз.

Замість цього просто називайте своє завдання на зразок new MyAsyncTask().execute("");

З документів AsyncTask API:

Правила нитки

Для правильної роботи цього класу необхідно дотримуватися декількох правил для нарізки:

  • Екземпляр завдання повинен бути створений у потоці інтерфейсу користувача.
  • Виконати (Params ...) потрібно викликати в потоці інтерфейсу користувача.
  • Не дзвоніть наPreExecute (), onPostExecute (Результат), doInBackground (Параметри ...), onProgressUpdate (Прогрес ...) вручну.
  • Завдання може бути виконано лише один раз (виняток буде викинуто, якщо буде здійснено друге виконання).

2
Це те, що я сказав, що я зробив, це єдина можливість? Тому що я хочу зберегти пам'ять, а не створювати новий об’єкт.
Дайерман


@StevePrentice: якщо я створюю екземпляр завдання кожні х секунд з новим завданням (). Виконувати (парам), надсилати дані на сервер, як збирач сміття може звільнити пам'ять після завершення виконання?
Ant4res

3
@ Ant4res, Поки ви не посилаєтесь на екземпляр завдання async, GC звільнить пам'ять. Однак якщо у вас є поточне фонове завдання, ви можете подумати про його виконання в циклі всередині doInBackground і здійснити дзвінки для публікації прогресу для оновлення прогресу. Або іншим підходом було б поставити своє завдання у фонову нитку. Тут існує безліч різних підходів, але не можна порекомендувати один одного без іншого.
Стів Прентіс

28

Причини виникнення вогнезахисних примірників ASyncTask досить детально описані у відповіді Стіва Прентіса - Однак, поки ви обмежені в тому, скільки разів виконайте ASyncTask, ви можете робити те, що вам подобається, поки нитка працює. .

Покладіть свій виконуваний код всередину циклу в doInBackground () та використовуйте паралельний замок для запуску кожного виконання. Ви можете отримати результати, використовуючи публікуватиProgress () / 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, і ви відмовляєтесь від використання публікаціїПрогрес () для фактичного звітування про прогрес. Але якщо пам'ять вас хвилює, то такий підхід забезпечить лише один ASyncTask, який залишається в купі під час виконання.


Але справа в тому, що я не хочу заново виконувати Асинтаск під час запуску, але тому, що цей завершив і не отримав дані як слід, потім зателефонуйте до нього ще раз.
Дайерман

Насправді ви виконаєте ASyncTask лише один раз таким чином, і ви можете перевірити, чи правильно вказані дані в методі onPublishProgress (або делегуєте чек деінде). Я використовував цю схему для подібної проблеми деякий час назад (велика кількість завдань, які виконуються швидко, послідовно, ризикуючи розміром купи).
seanhodges

Але що робити, якщо в цей момент сервер не відповів, і я хочу спробувати ще раз через 10 секунд? AsyncTask вже закінчився, rigth? Тоді мені потрібно його ще раз зателефонувати
Dayerman

Я додав код прикладу, щоб описати, що я маю на увазі. ASyncTask завершиться лише після того, як ви будете задоволені результатом і зателефонуєте "terminateTask ()".
seanhodges

1
Якщо ви отримуєте IllegalMonitorStateExceptionв runAgain(викликається onProgressUpdate) побачити цю відповідь: stackoverflow.com/a/42646476/2711811 . Це говорить про (і працював для мене), що signal()потрібно оточити а lock/ unlock. Це може бути пов’язано з часом publishProgressрозмови onProgressUpdate.
Енді

2

У мене було те саме питання. У моєму випадку у мене є завдання, яке я хочу робити в onCreate()і в onResume(). Тому я зробив свій Асинтактаск статичним і отримаю від нього екземпляр. Зараз у нас все ще така ж проблема.

Отже, що я зробив у onPostExecute (), це:

instance = null;

Маючи на увазі, що я перевіряю в статичному методі getInstance, що мій екземпляр не є нульовим, інакше я його створюю:

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

Метод у postExecute спорожнить екземпляр і відтворить його. Звичайно, це можна зробити поза класом.


1

Я зробив свої завдання на обертання статичними, що потім допомогло мені приєднати, від'єднати та приєднати їх до потоків інтерфейсу користувача при зміні обертання. Але повернутися до вашого питання, що я роблю, це створити прапор, щоб побачити, чи працює нитка. Коли ви хочете перезапустити потік, я перевіряю, чи виконується завдання обертання, чи це я тост, що попереджає. Якщо це не так, я роблю це недійсним, а потім створюю нове, яке обійде помилку, яку ви бачите. Крім того, після успішного завершення я скасовую завершене завдання, пов'язане з обертанням, щоб воно було готове перейти знову.


0

Так, це правда, доктор каже, що може бути виконаний лише один Асинтаск.

Кожен раз, коли вам потрібно це використовувати, ви повинні інстанцію:

// 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> {
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.