【问题标题】:Ideal way to cancel an executing AsyncTask取消正在执行的 AsyncTask 的理想方法
【发布时间】:2011-02-13 16:10:28
【问题描述】:

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

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

【问题讨论】:

    标签: android android-asynctask


    【解决方案1】:

    刚刚发现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;
        }
    
        // ...
    
    }
    

    【讨论】:

    • 而不是为 running 制作一个布尔标志,你不能​​将它删除并制作它 while(!isCanceled())???
    • 来自关于 onCancelled() 的文档:“在 UI 线程上运行 cancel(boolean) 被调用并且 doInBackground(Object[]) 已完成。”这个“之后”意味着在 onCancelled 中设置一个标志并检查 doInBackground 是没有意义的。
    • @confucius 没错,但是这样后台线程不会被中断,假设上传图片时,上传过程在后台继续,我们没有调用onPostExecute。
    • @DanHulme 我相信我指的是答案中提供的代码sn-p,而不是孔子的评论(这是正确的)。
    • 是的,这个答案不起作用。在 doInBackground 中,将 while(running) 替换为 while(!isCancelled()),正如其他人在 cmets 中所说的那样。
    【解决方案2】:

    如果您在进行计算

    • 您必须定期检查isCancelled()

    如果您正在执行 HTTP 请求

    • HttpGetHttpPost 的实例保存在某处(例如公共字段)。
    • 拨打cancel后,拨打request.abort()。这将导致IOException 被扔进你的doInBackground

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

    【讨论】:

    • 谢谢,可以,但是这种情况下如何避免异常呢?
    • 你必须从后台线程调用HttpGet.abort(),否则你会得到一个android.os.NetworkOnMainThreadException
    • @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.
    • HttpURLConnection.disconnect();
    • 如果你在AsyncTask中有cpu消耗操作,那么你必须调用cancel(true)。我用过,效果很好。
    【解决方案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;
            }
         }
    

    【讨论】:

    • 这将起作用,但从逻辑上讲,当您等待服务器响应并且您刚刚进行了数据库操作时,应该反映对您的活动的正确更改。我为此写了一篇博客,请参阅我的答案。
    • 如 cmets 在接受的答案中所述,无需创建自己的 running 标志。 AsyncTask 有一个内部标志,当任务被取消时设置。将while (running) 替换为while (!isCancelled())developer.android.com/reference/android/os/… 所以在这个简单的例子中,你不需要 onCancelled() 覆盖。
    【解决方案4】:

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

    【讨论】:

    • 您是否建议我们使用常规 Java 线程并“中止”使用 volatile 布尔变量运行的线程 - 传统的 Java 方式?
    • 无意冒犯迈克,但这不是一个可以接受的答案。 AsyncTask 有一个取消方法,它应该可以工作。据我所知,它没有——但即使我做错了,也应该有一个正确的方法来取消任务。否则该方法将不存在。即使是很短的任务也可能需要取消——我有一个 Activity,它在加载后立即开始一个 AsyncTask,如果用户在打开任务后立即回击,他们会在任务完成但没有上下文时在一秒钟后看到强制关闭存在以供其在其 onPostExecute 中使用。
    • @Klondike:我不知道“迈克”是谁。 “但这不是一个可以接受的答案” - 欢迎您提出意见。 “AsyncTask 有一个取消方法,它应该可以工作。” -- 取消 Java 中的线程已经成为一个问题大约 15 年了。它与Android没有太大关系。关于您的“强制关闭”场景,这可以通过一个布尔变量来解决,您可以在onPostExecute() 中对其进行测试,看看您是否应该继续工作。
    • @Tejaswi Yerukalapudi:更重要的是它不会自动执行任何操作。请参阅此问题的已接受答案。
    • 您应该定期在 AsyncTask 的 doInBackground 中检查 isCancelled 方法。它就在文档中:developer.android.com/reference/android/os/…
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

      • 我发现简单地在异步任务上调用 cancel(true) 并定期检查 isCancelled() 确实有效,但根据您的任务正在做什么,它可能需要长达 60 秒才能被中断。添加 Thread.sleep(1) 使其能够立即中断。 (异步任务进入等待状态,而不是立即丢弃)。谢谢你。
      【解决方案7】:

      我们的全局 AsyncTask 类变量

      LongOperation LongOperationOdeme = new LongOperation();
      

      以及中断 AsyncTask 的 KEYCODE_BACK 动作

         @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()。但至少保证异步任务终止。

        【讨论】:

        • 看起来像一个竞争条件。
        【解决方案9】:

        关于 Yanchenko 在 2010 年 4 月 29 日的回答: 当每次执行 AsyncTask 期间必须多次执行“doInBackground”下的代码时,使用“while(running)”方法很简洁。如果每次执行 AsyncTask 时必须只执行一次“doInBackground”下的代码,则将“doInBackground”下的所有代码包装在“while(running)”循环中不会阻止后台代码(后台线程)在AsyncTask 本身被取消,因为只有在 while 循环内的所有代码至少执行一次后才会评估“while(running)”条件。因此,您应该 (a.) 将“doInBackground”下的代码分解为多个“while(running)”块或 (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;}
        }
        
        // ...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-11-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多