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

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

#1 楼

刚刚发现我一直在使用的AlertDialogs实际上没有任何作用。太好了。
所以...

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

    // ...

}


评论


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

–孔子
2012年2月28日在9:06

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

– lopek
13年2月26日在0:15

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

–umesh
2014年1月21日,13:04

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

– lopek
2014年5月14日14:43

是的,此答案无效。在doInBackground中,将while(running)替换为while(!isCancelled()),就像其他人在评论中所说的那样。

–matt5784
15年8月13日在0:35

#2 楼

如果要进行计算:


必须定期检查isCancelled()

如果要进行HTTP请求:


HttpGetHttpPost的实例保存在某个地方(例如公共场所)。
调用cancel后,请调用request.abort()。这将导致IOException被抛出到您的doInBackground中。

在我的情况下,我有一个连接器类,该类在各种AsyncTask中使用。为了简单起见,我向该类添加了新的abortAllRequests方法,并在调用cancel之后直接调用了此方法。

评论


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

– begiPass
2013年11月1日13:44



您必须从后台线程调用HttpGet.abort(),否则将获得android.os.NetworkOnMainThreadException。

–希思边界
14年4月29日在18:40

@wrygiel如果您正在执行HTTP请求,是否不应该取消(true)中断请求?来自文档:如果任务已经开始,则mayInterruptIfRunning参数确定是否应中断执行该任务的线程以尝试停止该任务。

– Storo
16-2-12在14:30



HttpURLConnection.disconnect();

–奥德布雷纳
16年5月21日在6:23

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

– S.M. Mousavi
16年11月16日在12:52

#3 楼

关键是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;
        }
     }


评论


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

–Vikas
2011年8月3日在18:17

如有关已接受答案的评论中所述,无需创建自己的运行标志。 AsyncTask具有一个内部标志,该标志在取消任务时设置。用while(!isCancelled())替换while(运行)。 developer.android.com/reference/android/os/…因此,在这种简单情况下,您不需要onCancelled()重写。

–ToolmakerSteve
15年8月26日在17:38



#4 楼

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

评论


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

– Samuh
2010-4-29 14:57

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

–埃里克·米尔(Eric Mill)
10 Jul 27'3:01

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

– CommonsWare
10年7月27日在3:29

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

– CommonsWare
2011年5月2日15:06

您应该在AsyncTask的doInBackground中定期检查isCancelled方法。就在文档中:developer.android.com/reference/android/os/…

–克里斯托弗·佩里(Christopher Perry)
2011年11月17日,下午1:33

#5 楼

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

#6 楼

这就是我编写AsyncTask的方式。关键是add 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;
    }


评论


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

–约翰·史密斯(John J Smith)
2014年10月9日19:31



#7 楼

我们的全局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);
    }


它对我有用。

#8 楼

我不想强迫不必要地用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()。但是至少可以保证异步任务终止。

评论


看起来像是种族状况。

–天使
17年2月3日,0:10

#9 楼

关于扬先科在4月29日的回答:“ 10:
,当在每次执行AsyncTask期间必须多次执行“ doInBackground”下的代码时,使用“ while(running)”方法很方便。如果您在每次执行AsyncTask时在“ doInBackground”下的代码仅需执行一次,那么当您将“ doInBackground”下的所有代码包装在“ while(running)”循环中时,不会将后台代码(后台线程)停止运行。 AsyncTask本身被取消,因为只有在while循环中的所有代码至少执行一次之后,才会评估“ while(running)”条件。因此,您应该
(a。)将“ doInBackground”下的代码分解为多个“ while(运行)”块,或者
(b。)在整个“ doInBackground”代码中执行大量的“ 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;}
}

// ...