【问题标题】:Android: Cancel Async TaskAndroid:取消异步任务
【发布时间】:2011-05-18 02:13:27
【问题描述】:

我使用异步任务上传图像并获得一些结果。

上传图片时,我看到一个进度对话框,用 onPreExecute() 方法编写,如下所示:

    protected void onPreExecute() { 
         uploadingDialog = new ProgressDialog(MyActivity.this); 
         uploadingDialog.setMessage("uploading"); 
         uploadingDialog.setCancelable(true);
         uploadingDialog.show();
    }

好的,当我按下后退按钮时,显然对话框会因为 setCancelable(true) 而消失。

但是(显然)异步任务并没有停止。

那么我该如何解决这个问题?当我按下后退按钮时,我想取消对话框和异步任务。有什么想法吗?

编辑:找到解决方案。在下面查看我的答案。

【问题讨论】:

    标签: android asynchronous task back


    【解决方案1】:

    来自 SDK:

    取消任务

    可以通过调用 cancel(boolean) 随时取消任务。 调用此方法将导致后续调用 isCancelled() 返回真。

    调用此方法后,onCancelled(Object),而不是 onPostExecute(Object) 将在 doInBackground(Object[]) 返回后调用。

    为确保尽快取消任务, 您应该始终定期检查 isCancelled() 的返回值 doInBackground(Object[]),如果可能的话(例如在循环内。)

    所以你的代码适合对话监听器:

    uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
        public void onCancel(DialogInterface dialog) {
            myTask.cancel(true);
            //finish();
        }
    });
    

    现在,正如我之前在 SDK 中提到的,您必须检查任务是否被取消,为此您必须检查 onPreExecute() 方法中的 isCancelled()。 p>

    例如:

    if (isCancelled()) 
        break;
    else
    {
       // do your work here
    }
    

    【讨论】:

    • 是的,我同意你的观点,但问题是我没有在我的 doInBackground 上使用循环。我只是使用一些命令通过 ftp 上传图像。所以我想知道,像你说的那样,在我发送图像之前检查任务是否被取消有关系吗?
    • @mole363,您只需在 doInBackground 方法中使用 isCancelled() 来检查任务是否被取消。
    • 这不会取消网络操作,因为不涉及循环。
    • “必须检查 onPreExecute() 方法中的 isCancelled()。”...您能解释一下吗?就文档而言:“调用此方法将导致 onCancelled(Object) 被调用在 doInBackground(Object[]) 返回后的 UI 线程上。调用此方法保证永远不会调用 onPostExecute(Object)。调用此方法后,您应该定期检查 isCancelled() 返回的值从 doInBackground(Object[]) 到尽早完成任务。”
    • 这意味着一旦用户调用 mytask.cancel(true); 就会调用 onCancelled() 回调;在异步任务上
    【解决方案2】:

    找到解决方案: 我在上传Dialog.show() 之前添加了一个动作监听器,如下所示:

        uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
              public void onCancel(DialogInterface dialog) {
                  myTask.cancel(true);
                  //finish();
              }
        });
    

    这样,当我按下后退按钮时,上面的 OnCancelListener 会取消对话框和任务。如果您想在后按时完成整个活动,您也可以添加 finish()。请记住将您的异步任务声明为如下变量:

        MyAsyncTask myTask=null;
    

    然后像这样执行你的异步任务:

        myTask = new MyAsyncTask();
        myTask.execute();
    

    【讨论】:

    • 你的解决方案和我上面发布的添加动作监听器的解决方案有什么区别?
    • 您不必在显示对话框之前如此公开地设置取消侦听器,有一个show 方法接受取消侦听器作为其参数之一。
    • 正常操作下onPostExecute是否应该关闭对话框?
    【解决方案3】:

    我花了一段时间才弄清楚这一点,我想要的只是一个简单的例子来说明如何做到这一点,所以我想我会发布我是如何做到的。这是一些更新图书馆的代码,并有一个进度对话框显示已更新的图书数量,并在用户关闭对话框时取消:

    private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
        private ProgressDialog dialog = new ProgressDialog(Library.this);
        private int total = Library.instance.appState.getAvailableText().length;
        private int count = 0;
    
        //Used as handler to cancel task if back button is pressed
        private AsyncTask<Void, Integer, Boolean> updateTask = null;
    
        @Override
        protected void onPreExecute(){
            updateTask = this;
            dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            dialog.setOnDismissListener(new OnDismissListener() {               
                @Override
                public void onDismiss(DialogInterface dialog) {
                    updateTask.cancel(true);
                }
            });
            dialog.setMessage("Updating Library...");
            dialog.setMax(total);
            dialog.show();
        }
    
        @Override
        protected Boolean doInBackground(Void... arg0) {
                for (int i = 0; i < appState.getAvailableText().length;i++){
                    if(isCancelled()){
                        break;
                    }
                    //Do your updating stuff here
                }
            }
    
        @Override
        protected void onProgressUpdate(Integer... progress){
            count += progress[0];
            dialog.setProgress(count);
        }
    
        @Override
        protected void onPostExecute(Boolean finished){
            dialog.dismiss();
            if (finished)
                DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
            else 
                DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
        }
    }
    

    【讨论】:

      【解决方案4】:

      在您的活动中创建一些成员变量,例如

      YourAsyncTask mTask;
      Dialog mDialog;
      

      将这些用于您的对话和任务;

      在 onPause() 中只需调用

      if(mTask!=null) mTask.cancel(); 
      if(mDialog!=null) mDialog.dismiss();
      

      【讨论】:

        【解决方案5】:

        我想改进代码。当您取消aSyncTask 时,onCancelled()aSyncTask 的回调方法)会被自动调用,您可以在那里隐藏您的progressBarDialog

        您也可以包含此代码:

        public class information extends AsyncTask<String, String, String>
            {
                @Override
                protected void onPreExecute() {
                    super.onPreExecute();
                }
        
                @Override
                protected String doInBackground(String... arg0) {
                    return null;
                }
        
                @Override
                protected void onPostExecute(String result) {
                    super.onPostExecute(result);
                    this.cancel(true);
                }
        
                @Override
                protected void onProgressUpdate(String... values) {
                    super.onProgressUpdate(values);
                }
        
                @Override
                protected void onCancelled() {
                    Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
                    dialog.hide(); /*hide the progressbar dialog here...*/
                    super.onCancelled();
                }
        
            }
        

        【讨论】:

          【解决方案6】:

          在我使用 AsyncTask 的大部分时间里,我的业务逻辑都在一个单独的业务类上,而不是在 UI 上。在这种情况下,我无法在 doInBackground() 处进行循环。一个例子是一个同步过程,它一个接一个地使用服务和持久化数据。

          我最终将我的任务交给了业务对象,以便它可以处理取消。我的设置是这样的:

          public abstract class MyActivity extends Activity {
          
              private Task mTask;
              private Business mBusiness;
          
              public void startTask() {
                  if (mTask != null) {
                      mTask.cancel(true);
                  }
                  mTask = new mTask();
                  mTask.execute();
              }
          }
          
          protected class Task extends AsyncTask<Void, Void, Boolean> {
              @Override
              protected void onCancelled() {
                  super.onCancelled();
          
                  mTask.cancel(true);
          
                  // ask if user wants to try again
              }
          
              @Override
              protected Boolean doInBackground(Void... params) {
                  return mBusiness.synchronize(this);
              }
          
              @Override
              protected void onPostExecute(Boolean result) {
                  super.onPostExecute(result);
          
                  mTask = null;
          
                  if (result) {
                      // done!
                  }
                  else {
                      // ask if user wants to try again
                  }
              }
          }
          
          public class Business {
              public boolean synchronize(AsyncTask<?, ?, ?> task) {
                  boolean response = false;
                  response = loadStuff(task);
          
                  if (response)
                      response = loadMoreStuff(task);
          
                  return response;
              }
          
              private boolean loadStuff(AsyncTask<?, ?, ?> task) {
                  if (task != null && task.isCancelled()) return false;
          
                  // load stuff
          
                  return true;
              }
          }
          

          【讨论】:

            【解决方案7】:

            我遇到了类似的问题 - 本质上,在用户销毁活动后,我在异步任务中获得了 NPE。在 Stack Overflow 上研究了这个问题后,我采用了以下解决方案:

            volatile boolean running;
            
            public void onActivityCreated (Bundle savedInstanceState) {
                super.onActivityCreated(savedInstanceState);
            
                running=true;
                ...
                }
            
            
            public void onDestroy() {
                super.onDestroy();
            
                running=false;
                ...
            }
            

            然后,我会定期在我的异步代码中检查“是否正在运行”。我已经对此进行了压力测试,现在我无法“破坏”我的活动。这完美地工作并且具有比我在 SO 上看到的一些解决方案更简单的优点。

            【讨论】:

              【解决方案8】:

              您可以只要求取消,但不能真正终止它。看到这个answer

              【讨论】:

                【解决方案9】:

                如何取消 AsyncTask

                完整答案在这里 - Android AsyncTask Example

                AsyncTask 提供了更好的取消策略,终止当前正在运行的任务。

                cancel(boolean mayInterruptIfitRunning)

                myTask.cancel(false)- 它使 isCancelled 返回 true。有助于取消任务。

                myTask.cancel(true) – 也让isCancelled() 返回true,中断后台线程,释放资源。

                这被认为是一种傲慢的方式,如果后台线程中有任何thread.sleep()方法执行,cancel(true)会在那个时候中断后台线程。但是 cancel(false) 将等待它并在该方法完成时取消任务。

                如果您调用 cancel() 并且 doInBackground() 尚未开始执行。 onCancelled() 将调用。

                在调用 cancel(...) 之后,您应该定期检查 doInbackground() 上 isCancelled() 返回的值。如下图所示。

                protected Object doInBackground(Params… params)  { 
                 while (condition)
                {
                 ...
                if (isCancelled()) 
                break;
                }
                return null; 
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2014-08-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多