Android/AsyncTask:还要检查"isCancelled"(API24)吗?

Android/AsyncTask: Do you still have to check "isCancelled" (API 24)?

我的应用程序使用 AsyncTask 下载文件,同时显示带有 "Cancel" 按钮的 ProgressDialog(我知道它已被弃用)。

根据 this,您应该定期检查 doInBackground 中的 isCancelled(),因为 mytask.cancel(true) 不会自行中断 doInBackground

我一开始没有检查就取消了任务,发现它仍然停止 doInBackground:根据我在按下 "Cancel" 按钮之前让它下载的时间长短,我看到了不同的大小在生成的文件中 - 从几 kb 到几 mb - 最终大小约为 9mb。

这怎么可能?你真的不用再打电话给 isCancelled() 了吗?

我的异步任务:

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

According to this you should check isCancelled() in doInBackground periodically because mytask.cancel(true) won't interrupt doInBackground on its own.

事实并非如此。

根据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.

这意味着您可以 另外 检查 isCancelled() 以停止 AsyncTask 更早 如果它已启动。

mytask.cancel(true) 无论如何都会停止执行。

让我们看看幕后发生了什么

当您调用 mytask.cancel(true) 时:

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

其中 mFutureFutureTask 内部可运行的

然后mFuture.cancel被调用:

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

其中 runner 只是

private volatile Thread runner;

因为它只是线程,让我们看看 interrupt 在你的情况下做了什么:

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.

因此,如果您的 download() 方法使用 InterruptibleChannel interrupt 将起作用。

换句话说,您似乎从来不需要调用 isCancelled() 来中断 AsyncTask =) 因为 Thread.interrupt 可以在您的情况下停止 io 阻塞操作。