取消执行中的AsyncTask的理想方法


108

我正在使用的后台线程中运行远程音频文件获取和音频文件回放操作AsyncTask。甲Cancellable进度条示出了用于提取操作的运行时间。

我想AsyncTask在用户取消(反对)操作时取消/中止运行。处理这种情况的理想方法是什么?

Answers:


76

才发现,原来AlertDialogsboolean cancel(...);我已经用实际处处什么也不做。大。
所以...

public class MyTask extends AsyncTask<Void, Void, Void> {

    private volatile boolean running = true;
    private final ProgressDialog progressDialog;

    public MyTask(Context ctx) {
        progressDialog = gimmeOne(ctx);

        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                // actually could set running = false; right here, but I'll
                // stick to contract.
                cancel(true);
            }
        });

    }

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected void onCancelled() {
        running = false;
    }

    @Override
    protected Void doInBackground(Void... params) {

        while (running) {
            // does the hard work
        }
        return null;
    }

    // ...

}

55
而不是为运行创建布尔标志,您是否不能删除它并使它成为while(!isCanceled())?
孔子2012年

36
来自有关onCancelled()的文档:“在调用cancel(boolean)和doInBackground(Object [])完成在UI线程上运行。” 此“之后”意味着在onCancelled中设置标志并在doInBackground中进行签入是没有意义的。
lopek

2
@confucius没错,但是这样背景线程不会被中断,假设在上载图像的情况下,上载过程在后台继续进行,而我们没有调用onPostExecute。
umesh 2014年

1
@DanHulme我相信我指的是答案中提供的代码片段,而不是孔子的评论(正确)。
lopek

4
是的,此答案无效。在doInBackground,更换while(running)while(!isCancelled())正如其他人在这里的评论说。
matt5784

76

如果您正在进行计算

  • 您必须isCancelled()定期检查。

如果您正在执行HTTP请求

  • 保存您的实例HttpGetHttpPost某处(例如公共场所)。
  • 致电后cancel,致电request.abort()。这将导致IOException您的计算机内部被抛出doInBackground

就我而言,我有一个用于各种AsyncTasks的连接器类。为简单起见,我abortAllRequests在该类中添加了一个新方法,并在调用之后直接调用了该方法cancel


谢谢,它有效,但是在这种情况下如何避免异常?
begiPass 2013年

您必须HttpGet.abort()从后台线程进行调用,否则会收到android.os.NetworkOnMainThreadException
Heath Borders

@wrygiel如果您正在执行HTTP请求,是否应该cancel(true)中断请求?来自文档:If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Storo

HttpURLConnection.disconnect();
Oded Breiner '16

如果您在AsyncTask中有消耗CPU的操作,则必须调用cancel(true)。我用它,它的工作原理。
SMMousavi '16

20

关键是AsyncTask.cancel()调用仅调用任务中的onCancel函数。这是您要处理取消请求的地方。

这是我用来触发更新方法的小任务

private class UpdateTask extends AsyncTask<Void, Void, Void> {

        private boolean running = true;

        @Override
        protected void onCancelled() {
            running = false;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            onUpdate();
        }

        @Override
        protected Void doInBackground(Void... params) {
             while(running) {
                 publishProgress();
             }
             return null;
        }
     }

2
这将起作用,但是在逻辑上,当您等待服务器响应时,并且您刚刚执行了db操作,则应该反映对活动的正确更改。我为此写了一个博客,请参阅我的答案。
维卡斯

4
如对已接受答案的评论中所述,无需创建自己的running标志。AsyncTask具有一个内部标志,该标志在取消任务时设置。替换while (running)while (!isCancelled())developer.android.com/reference/android/os/… 因此,在这种简单情况下,您不需要onCancelled()覆盖。
制造商史蒂夫(Steve)2015年

11

简单:请勿使用AsyncTaskAsyncTask专为快速结束(数十秒)的短操作而设计,因此不需要取消。“音频文件播放”不符合条件。您甚至不需要后台线程即可播放普通音频文件。


您是否建议我们使用常规的Java线程并使用volatile布尔变量“中止”运行线程-传统的Java方法?
Samuh'4

34
没有冒犯迈克,但这不是一个可以接受的答案。AsyncTask有一个cancel方法,它应该可以工作。据我所知,这不是-但是即使我做错了,也应该有取消任务的正确方法。否则该方法将不存在。甚至是简短的任务也可能需要取消-我有一个Activity,它在加载后立即开始AsyncTask,如果用户在打开任务后立即回击,他们将在任务完成但没有上下文的情况下看到强制关闭一秒钟存在于它的onPostExecute中。
埃里克·米尔2010年

10
@克朗代克:我不知道谁是“麦克”。“但这不是可接受的答案”-欢迎您提出意见。“ AsyncTask有一个cancel方法,它应该可以工作。” -在Java中取消线程已经有15年了。它与Android无关。关于“强制关闭”方案,可以通过布尔变量来解决,您可以对布尔变量进行测试,onPostExecute()以查看是否应该继续进行工作。
CommonsWare 2010年

1
@Tejaswi Yerukalapudi:更多的是它不会自动执行任何操作。请参阅有关此问题的公认答案。
CommonsWare

10
您应该在AsyncTask的doInBackground中定期检查isCancelled方法。这是正确的,在文档:developer.android.com/reference/android/os/...
克里斯托弗·佩里

4

唯一的方法是检查isCancelled()方法的值,并在返回true时停止播放。


4

这就是我编写AsyncTask
的方式,关键是添加Thread.sleep(1);

@Override   protected Integer doInBackground(String... params) {

        Log.d(TAG, PRE + "url:" + params[0]);
        Log.d(TAG, PRE + "file name:" + params[1]);
        downloadPath = params[1];

        int returnCode = SUCCESS;
        FileOutputStream fos = null;
        try {
            URL url = new URL(params[0]);
            File file = new File(params[1]);
            fos = new FileOutputStream(file);

            long startTime = System.currentTimeMillis();
            URLConnection ucon = url.openConnection();
            InputStream is = ucon.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);

            byte[] data = new byte[10240]; 
            int nFinishSize = 0;
            while( bis.read(data, 0, 10240) != -1){
                fos.write(data, 0, 10240);
                nFinishSize += 10240;
                **Thread.sleep( 1 ); // this make cancel method work**
                this.publishProgress(nFinishSize);
            }              
            data = null;    
            Log.d(TAG, "download ready in"
                  + ((System.currentTimeMillis() - startTime) / 1000)
                  + " sec");

        } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                returnCode = FAIL;
        } catch (Exception e){
                 e.printStackTrace();           
        } finally{
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                e.printStackTrace();
            }
        }

        return returnCode;
    }

1
我发现只需在异步任务上调用cancel(true)并定期检查isCancelled()即可正常工作,但根据您任务的执行情况,最多可能需要60秒才能将其中断。添加Thread.sleep(1)使其立即中断。(异步任务宁可进入等待状态,也不会立即丢弃)。谢谢你
约翰·J·史密斯

0

我们的全局AsyncTask类变量

LongOperation LongOperationOdeme = new LongOperation();

和KEYCODE_BACK动作会中断AsyncTask

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            LongOperationOdeme.cancel(true);
        }
        return super.onKeyDown(keyCode, event);
    }

这个对我有用。


0

我不想强迫cancel(true)不必要地中断异步任务,因为它们可能有释放的资源,例如关闭套接字或文件流,将数据写入本地数据库等。另一方面,我遇到了这样的情况:异步任务拒绝部分时间自己完成任务,例如有时在关闭主活动时,我要求从活动onPause()方法内部完成异步任务。因此,这不是简单地调用的问题running = false。我必须选择一种混合解决方案:都调用running = false,然后给异步任务几毫秒以完成,然后调用cancel(false)cancel(true)

if (backgroundTask != null) {
    backgroundTask.requestTermination();
    try {
        Thread.sleep((int)(0.5 * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (backgroundTask.getStatus() != AsyncTask.Status.FINISHED) {
        backgroundTask.cancel(false);
    }
    backgroundTask = null;
}

附带的结果是,doInBackground()完成后有时会onCancelled()调用方法,有时会调用onPostExecute()。但是至少可以保证异步任务终止。


看起来像一个种族条件。
msangel

0

关于Yanchenko在10月4月29日的回答:使用'while(running)'方法很巧妙,因为在每次执行AsyncTask期间必须多次执行'doInBackground'下的代码。如果您每次执行AsyncTask时在“ doInBackground”下的代码仅需执行一次,则在“ AsyncTask本身被取消,因为只有在while循环中的所有代码至少执行一次之后,才会评估“ while(running)”条件。因此,您应该(a。)将“ doInBackground”下的代码分解为多个“ while(运行)”块,或者(b。)执行多个“ isCancelled”https://developer.android.com/reference/android/os/AsyncTask.html

对于选项(a。),可以这样修改Yanchenko的答案,如下所示:

public class MyTask extends AsyncTask<Void, Void, Void> {

private volatile boolean running = true;

//...

@Override
protected void onCancelled() {
    running = false;
}

@Override
protected Void doInBackground(Void... params) {

    // does the hard work

    while (running) {
        // part 1 of the hard work
    }

    while (running) {
        // part 2 of the hard work
    }

    // ...

    while (running) {
        // part x of the hard work
    }
    return null;
}

// ...

对于选项(b。),您在'doInBackground'中的代码将如下所示:

public class MyTask extends AsyncTask<Void, Void, Void> {

//...

@Override
protected Void doInBackground(Void... params) {

    // part 1 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // part 2 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // ...

    // part x of the hard work
    // ...
    if (isCancelled()) {return null;}
}

// ...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.