0

If I have a button action that launches an AsyncTask. What if I click the button 10 times? Is the task executes 10 times and runs that much in background? Or is any previous task canceled and just the task reinitiated (that would be my desired behaviour)?

public void myButtonClick(View v) {
   new MyAsyncTask().execute(params);
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • 1
    It executes 10 times indeed. If you want it to cancel, you have to do it manually (call `cancel` on it). See [Cancelling a Task](http://developer.android.com/reference/android/os/AsyncTask.html). – user703016 May 20 '12 at 22:54
  • 1
    @membersound : Although you've accepted an answer to the specific question it isn't really a good design approach. Ideally I would disable the button on first click in order to *prevent* a user clicking it 10 times. If you would like a user to be able to cancel the `AsyncTask` then change the button text to `Cancel` so they realise what is happening. Another approach would be a progress dialog of some sort which would steal focus from the `Activity` hosting the button (thus preventing multiple presses) and could also have a `Cancel` or `Restart` button. – Squonk May 20 '12 at 23:31
  • OK this might be a good approach. But for this special case I really need to cancel a task if the task is reinitiated. – membersound May 21 '12 at 00:04

2 Answers2

2

Each click on the button is producing a new instance of your MyAsyncTask, that means that each task is created and executed until its ends.

If you want to stop a previously launched task, you have to store a reference to the last launched task and cancel it like:

public void myButtonClick(View v) {
    if (this.lastMyAsyncTask != null && this.lastMyAsyncTask.getStatus() != AsyncTask.Status.FINISHED){
        this.lastMyAsyncTask.cancel();
    }
    this.lastMyAsyncTask = new MyAsyncTask();
    this.lastMyAsyncTask.execute(params);
}
ChristopheCVB
  • 7,269
  • 1
  • 29
  • 54
  • Many thanks that helped a lot! Just for reference, I found that one have to call `lastMyAsyncTask.cancel(true);` – membersound May 20 '12 at 23:19
  • 1
    /edit: it does not work! task are still running in background until they completel... – membersound May 20 '12 at 23:38
  • You cannot abort threads (this includes the doInBackground method of AsyncTask). See my answer here for more http://stackoverflow.com/questions/9119741/listadapter-lifecycle-and-screen-rotation-for-canceling-a-asynctask/9122479#9122479 – Lawrence D'Oliveiro May 21 '12 at 11:40
1

If you really want to keep the task to a single execution at a time, you should create a single threaded ThreadPool:

    private ThreadPoolExecutor executor = 
        new ThreadPoolExecutor(1,1, 1, TimeUnit.SECONDS, 
             new SynchronousQueue(), new ThreadPoolExecutor.AbortPolicy());
    private Future<Void> lastSubmitted;
    private ReentrantLock submissionLock = new ReentrantLock();
    ...


    try {
      submissionLock.lock();
      Future<Void> future = 
        executor.submit(new MyAsyncTask()); // assuming this implements Callable<Void>
      lastSubmitted = future;
    }
    catch (RejectedExecutionException e) {
      lastSubmitted.cancel();
      Future<Void> future = 
        executor.submit(new MyAsyncTask()); // assuming this implements Callable<Void>
      lastSubmitted = future;

    }
    finally {
      submissionLock.unlock();
    }

If you do it as suggested above, you'll get a RejectedExecutionException if you try to submit a task while one is already running. When that happens, you can cancel the last one submitted, and schedule a new execution. Keep in mind your task has to be interruptible (be careful with InputStream/OutputStream as they are not interruptible, use Channel objects instead).

The lock is needed so that you serialize the submission attempts. A thread could still get a RejectedExecutionException if another thread submitted a task during the catch block execution.

Matt
  • 11,523
  • 2
  • 23
  • 33