Another nice usability touch we can provide for our users is the ability to cancel a task before it completes—for example, if the task depends on some user input and, after starting the execution, the user realizes that they have provided the wrong value. AsyncTask
provides support for cancellation with the cancel
method.
public final boolean cancel(boolean mayInterruptIfRunning)
The mayInterruptIfRunning
parameter allows us to specify whether an AsyncTask
thread that is in an interruptible state may actually be interrupted—for example, if our doInBackground
code is performing interruptible I/O.
Simply invoking cancel
is not sufficient to cause our task to finish early. We need to actively support cancellation by periodically checking the value returned from isCancelled
and reacting appropriately in doInBackground
.
First, let's set up our ProgressDialog
to trigger the AsyncTask's cancel
method by adding a few lines to onPreExecute
:
progress.setCancelable(true); progress.setOnCancelListener( new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { PrimesTask.this.cancel(false); } });
Now we can trigger cancel
by touching outside the progress dialog, or pressing the device's back button while the dialog is visible. We'll invoke cancel
with false
, as we are not doing interruptible work inside the method or checking the return value of Thread.interrupted
, so calling an interrupt will have no effect. We still need to check for cancellation in doInBackground
, so we will modify it as follows:
protected BigInteger doInBackground(Integer... params) { int primeToFind = params[0]; BigInteger prime = new BigInteger("2"); for (int i=0; i<primeToFind; i++) { prime = prime.nextProbablePrime(); int percentComplete = (int)((i * 100f)/primeToFind); publishProgress(percentComplete); if (isCancelled()) break; } return prime; }
The cancelled AsyncTask
does not receive the onPostExecute
callback. Instead, we have the opportunity to implement different behavior for a cancelled execution by implementing onCancelled
. There are two variants of this callback method:
protected void onCancelled(Result result); protected void onCancelled();
The default implementation of the parameterized onCancelled(Result result)
method delegates to the onCancelled
method.
If AsyncTask
can provide either a complete result (such as a fully downloaded image) or nothing, then we will probably want to override the zero argument onCancelled()
method.
If we are performing an incremental computation in our AsyncTask
, we might choose to override the onCancelled(Result result)
version so that we can make use of the result computed up to the point of cancellation.
In all cases, since onPostExecute
does not get called on a canceled AsyncTask
, we will want to make sure that our onCancelled
callbacks update the user interface appropriately—in our example, this entails dismissing the progress dialog we opened in onPreExecute
, and updating the result text.
protected void onCancelled(BigInteger result) { if (result != null) resultView.setText("cancelled at " + result.toString()); progress.dismiss(); }
Another situation to be aware of occurs when we cancel an AsyncTask
that has not yet begun its doInBackground
method. If this happens, doInBackground
will never be invoked, though onCancelled
will still be called on the main thread.
3.147.126.33