首页 > 解决方案 > Android/AsyncTask:你还需要检查“isCancelled”(API 24)吗?

问题描述

我的应用程序使用 anAsyncTask来下载文件,同时显示ProgressDialog带有“取消”按钮的(我知道它已被弃用)。

根据这一点,您应该定期检查isCancelled(),因为不会自行中断。doInBackgroundmytask.cancel(true)doInBackground

我一开始没有检查就简单地取消了任务,并注意到它仍然停止doInBackground:根据我在按下“取消”按钮之前让它下载多长时间,我在生成的文件中看到了不同的大小——从几 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();
        //....
    }

标签: androidandroid-asynctaskcancellation

解决方案


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

实际上这不是真的。

根据文件

调用此方法后,应定期从 doInBackground(Object[]) 中检查 isCancelled() 返回的值,以尽早完成任务。

这意味着您还可以检查是否已启动更早isCancelled()停止。AsyncTask

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

让我们看看引擎盖下发生了什么

当你打电话时mytask.cancel(true)

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

在哪里mFuture可以FutureTask运行

然后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你的情况:

如果此线程在可中断通道上的 I/O 操作中被阻塞,则通道将关闭,线程的中断状态将被设置,并且线程将收到 ClosedByInterruptException。

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

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


推荐阅读