Execute AsyncTask several times

In my Activity I use a class which extends from AsyncTask and a parameter which is an instance of that AsyncTask. When I call mInstanceOfAT.execute("") everything is fine. But the app crash when I press an update button which calls again the AsyncTask(In case the network job didnt work). Cause then appears an Exception which says

Cannot execute task: the task has already been executed (a task can be executed only once)

I have tried calling cancel(true) for the instance of the Asyctask, but it doesnt work either. The only solution so far it's to create new instances of the Asyntask. Is that the correct way?

Thanks.


Solution 1:

AsyncTask instances can only be used one time.

Instead, just call your task like new MyAsyncTask().execute("");

From the AsyncTask API docs:

Threading rules

There are a few threading rules that must be followed for this class to work properly:

  • The task instance must be created on the UI thread.
  • execute(Params...) must be invoked on the UI thread.
  • Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
  • The task can be executed only once (an exception will be thrown if a second execution is attempted.)

Solution 2:

The reasons for fire-and-forget instances of ASyncTask are detailed pretty well in Steve Prentice's answer - However, whilst you are restricted on how many times you execute an ASyncTask, you are free to do what you like whilst the thread is running...

Put your executable code inside a loop within doInBackground() and use a concurrent lock to trigger each execution. You can retrieve the results using publishProgress()/onProgressUpdate().

Example:

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

Of course, this is slightly more complicated than the traditional usage of ASyncTask, and you give up the use of publishProgress() for actual progress reporting. But if memory is your concern, then this approach will ensure only one ASyncTask remains in the heap at runtime.