Android 取消正在执行的 AsyncTask 的理想方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2735102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:09:16  来源:igfitidea点击:

Ideal way to cancel an executing AsyncTask

androidandroid-asynctask

提问by Samuh

I am running remote audio-file-fetching and audio file playback operations in a background thread using AsyncTask. A Cancellableprogress bar is shown for the time the fetch operation runs.

我正在使用AsyncTask. 甲Cancellable进度条示出了用于提取操作的运行时间。

I want to cancel/abort the AsyncTaskrun when the user cancels (decides against) the operation. What is the ideal way to handle such a case?

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

采纳答案by yanchenko

Just discovered that AlertDialogs's boolean cancel(...);I've been using everywhere actually does nothing. Great.
So...

才发现,原来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;
    }

    // ...

}

回答by wrygiel

If you're doing computations:

如果你在做计算

  • You have to check isCancelled()periodically.
  • 您必须isCancelled()定期检查。

If you're doing a HTTP request:

如果您正在执行 HTTP 请求

  • Save the instance of your HttpGetor HttpPostsomewhere (eg. a public field).
  • After calling cancel, call request.abort(). This will cause IOExceptionbe thrown inside your doInBackground.
  • 保存您的HttpGetHttpPost某处的实例(例如公共字段)。
  • 打电话后cancel,打电话request.abort()。这将导致IOException在您的doInBackground.

In my case, I had a connector class which I used in various AsyncTasks. To keep it simple, I added a new abortAllRequestsmethod to that class and called this method directly after calling cancel.

就我而言,我有一个在各种 AsyncTask 中使用的连接器类。为了简单起见,我向abortAllRequests该类添加了一个新方法,并在调用cancel.

回答by DonCroco

The thing is that AsyncTask.cancel() call only calls the onCancel function in your task. This is where you want to handle the cancel request.

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

Here is a small task I use to trigger an update method

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

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

回答by CommonsWare

Simple: don't use an AsyncTask. AsyncTaskis designed for short operations that end quickly (tens of seconds) and therefore do not need to be canceled. "Audio file playback" does not qualify. You don't even need a background thread for ordinary audio file playback.

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

回答by dbyrne

The only way to do it is by checking the value of the isCancelled() method and stopping playback when it returns true.

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

回答by Andrew Chen

This is how I write my AsyncTask
the key point is add Thread.sleep(1);

这就是我编写 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;
    }

回答by G?ksel Güren

Our global AsyncTask class variable

我们的全局 AsyncTask 类变量

LongOperation LongOperationOdeme = new LongOperation();

And KEYCODE_BACK action which interrupt AsyncTask

和中断 AsyncTask 的 KEYCODE_BACK 动作

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

It works for me.

这个对我有用。

回答by Piovezan

I don't like to force interrupt my async tasks with cancel(true)unnecessarily because they may have resources to be freed, such as closing sockets or file streams, writing data to the local database etc. On the other hand, I have faced situations in which the async task refuses to finish itself part of the time, for example sometimes when the main activity is being closed and I request the async task to finish from inside the activity's onPause()method. So it's not a matter of simply calling running = false. I have to go for a mixed solution: both call running = false, then giving the async task a few milliseconds to finish, and then call either cancel(false)or cancel(true).

我不喜欢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;
}

As a side result, after doInBackground()finishes, sometimes the onCancelled()method is called, and sometimes onPostExecute(). But at least the async task termination is guaranteed.

结果是,在doInBackground()完成后,有时会onCancelled()调用该方法,有时会调用onPostExecute(). 但至少保证异步任务终止。

回答by Alex Ivan Howard

With reference to Yanchenko's answer on 29 April '10: Using a 'while(running)' approach is neat when your code under 'doInBackground' has to be executed multiple times during every execution of the AsyncTask. If your code under 'doInBackground' has to be executed only once per execution of the AsyncTask, wrapping all your code under 'doInBackground' in a 'while(running)' loop will not stop the background code (background thread) from running when the AsyncTask itself is cancelled, because the 'while(running)' condition will only be evaluated once all the code inside the while loop has been executed at least once. You should thus either (a.) break up your code under 'doInBackground' into multiple 'while(running)' blocks or (b.) perform numerous 'isCancelled' checks throughout your 'doInBackground' code, as explained under "Cancelling a task" at https://developer.android.com/reference/android/os/AsyncTask.html.

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

For option (a.) one can thus modify Yanchenko's answer as follows:

对于选项 (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;
}

// ...

For option (b.) your code in 'doInBackground' will look something like this:

对于选项 (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;}
}

// ...