【问题标题】:Android/AsyncTask: Do you still have to check "isCancelled" (API 24)?Android/AsyncTask:你还需要检查“isCancelled”(API 24)吗?
【发布时间】:2018-11-13 23:40:10
【问题描述】:

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

根据this,您应该定期检查doInBackground 中的isCancelled(),因为mytask.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();
        //....
    }

【问题讨论】:

    标签: android android-asynctask cancellation


    【解决方案1】:

    据此,您应该在 doInBackground 中检查 isCancelled() 定期因为 mytask.cancel(true) 不会中断 doInBackground 本身。

    其实不然。

    根据documentation

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

    这意味着您可以另外检查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 在你的情况下做了什么:

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

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

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

    【讨论】:

    • 谢谢!是的,你是对的,我以前从来没有打电话给isCancelled()。 ;) 所以 baiscally mytask.cancel(true) 只是一种通过中断线程来取消 I/O 操作(在我的例子中:BufferedOutputStream)的方法。如果没有 I/O 操作正在进行但例如不想阻塞 UI 线程的长计算?你还能用cancel(true) 阻止这些,还是在这种情况下你真的必须继续检查isCancelled()
    • @Neph 长操作在 doInBackground 中,和在单独线程中一样不能阻塞 UI 线程。此外,即使您不检查 isCancelled 线程也会被中断,例如 doInBackground 中的 while(true){} 无论如何都会被中断。但是如果你写 (while(!isCancelled()) {} 代替,即使你写 cancel(false) 也会正确停止。
    • 对不起,我的意思是在 AsyncTask 中有一个长的非 I/O 操作,所以它不会阻塞 UI 线程。对我来说这是一个混合体,首先我连接到服务器,这可能需要一点时间(但会自行超时),然后我正在读取/写入(=下载)文件。最后,没有什么真正无限的事情发生,这正是我收集到的 AsyncTask 应该用于的用途。很高兴知道,谢谢!
    猜你喜欢
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 2013-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-23
    • 2021-09-20
    相关资源
    最近更新 更多