Issue
My app uses an AsyncTask
to download files while displaying a ProgressDialog
(I'm aware that it's deprecated) with a "Cancel" button.
According to this you should check isCancelled()
in doInBackground
periodically because mytask.cancel(true)
won't interrupt doInBackground
on its own.
I simply cancelled the task without checking at first and noticed that it still stops doInBackground
: Depending on how long I let it download before pressing the "Cancel" button, I've seen different sizes in the resulting file - from just a few kb to a couple of mb - the final size would have been around 9mb.
How is this possible? Do you actually not have to call isCancelled()
anymore?
My AsyncTask:
private class DownloadTask extends AsyncTask<String, String, String> {
protected void onPreExecute() {
progressdialog.setMessage("Preparing Download...");
progressdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressdialog.setProgressNumberFormat(null);
progressdialog.setProgressPercentFormat(null);
progressdialog.setIndeterminate(true);
progressdialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
progressdialog.dismiss();
mytask.cancel(true);
}
});
progressdialog.show();
}
protected String doInBackground(String... bla) {
String error = download();
return error;
}
protected void onProgressUpdate(String... s) {
//....
}
protected void onPostExecute(String s) {
progressdialog.dismiss();
//....
}
Solution
According to this you should check isCancelled() in doInBackground periodically because mytask.cancel(true) won't interrupt doInBackground on its own.
Actually it is not true.
According to documentation:
After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
It means you can additionally check for isCancelled()
to stop AsyncTask
earlier if it is started.
mytask.cancel(true) will stop execution anyway.
Let`s see under the hood what is going on
When you call mytask.cancel(true)
:
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
Where mFuture
is FutureTask
that holds runnable inside
Then mFuture.cancel
is called:
public boolean cancel(boolean mayInterruptIfRunning) {
if (state != NEW)
return false;
if (mayInterruptIfRunning) {
if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
return false;
Thread t = runner;
if (t != null)
t.interrupt();
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
}
else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
return false;
finishCompletion();
return true;
}
Where runner
is just
private volatile Thread runner;
Since its just thread, lets see what interrupt
does in your case:
If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.
So if your download()
method uses InterruptibleChannel
interrupt
will work.
In other words looks like you have never had to call isCancelled()
to interrupt AsyncTask
=) since Thread.interrupt
can stop io blocking operation in your case.
Answered By - Andrey Danilov
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.