Android - 强力取消AsyncTask

时间:2011-01-20 15:26:48

标签: android android-asynctask

我在我的一个活动中实现了AsyncTask:

 performBackgroundTask asyncTask = new performBackgroundTask();
 asyncTask.execute();

现在,我需要实现“取消”按钮功能,所以我必须停止执行正在运行的任务。我不知道如何停止正在运行的任务(后台任务)。

所以请建议我,如何有力地取消AsyncTask?

更新

我发现了相同的Cancel()方法,但我发现调用 cancel(boolean mayInterruptIfRunning) 并不一定会停止执行后台进程。似乎发生的一切是AsyncTask将执行onCancelled(),并且在完成时不会运行onPostExecute()。

6 个答案:

答案 0 :(得分:89)

偶尔检查isCancelled()

 protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled()) break;
    }
    return null;
 }

答案 1 :(得分:49)

致电cancel()上的AsyncTask。这是否会实际取消任何东西取决于你正在做什么。引用罗曼盖伊:

  

如果您调用cancel(true),则为中断   将被发送到后台主题,   这可能有助于可中断的任务。   否则,你应该确保   定期检查isCancelled()   你的doInBackground()方法。您可以   看看这个例子   code.google.com/p/shelves。

答案 2 :(得分:16)

这实际上取决于你在asynctask中做了什么。

如果它是一个处理大量文件的循环,你可以在每个文件之后检查isCanceled()标志是否被引发,然后如果是,则从你的循环中断。

如果它是执行非常长时间操作的一行命令,那么你无能为力。

最好的解决方法是不使用asynctask的cancel方法并使用你自己的cancelFlag布尔值。 然后,您可以在postExecute中测试此cancelFlag,以决定如何处理结果。

答案 3 :(得分:5)

在评论案例中提到isCancelled() always returns false even i call asynctask.cancel(true);如果我关闭我的应用程序特别有害,但AsyncTask继续有效。

为了解决这个问题,我通过以下方式修改了Jacob Nordfalk代码:

protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled() || (FlagCancelled == true)) break;
    }
    return null;
 }

并将以下内容添加到主要活动中:

@Override
protected void onStop() {
    FlagCancelled = true;
    super.onStop();
}

由于我的AsyncTask是其中一个视图的私有类,因此需要该标志的getter或setter来通知AsyncTask当前实际的标志值。

我的多次测试(AVD Android 4.2.2,Api 17)表明,如果AsyncTask已经在执行其doInBackground,那么isCancelled()不会做出任何反应(即继续为假)任何取消它的尝试,例如在mViewGroup.removeAllViews();期间或OnDestroy MainActivity期间,每个导致视图分离

   @Override 
   protected  void  onDetachedFromWindow() { 
    mAsyncTask.cancel(false); // and the same result with mAsyncTask.cancel(true);
    super.onDetachedFromWindow(); 
   } 

如果由于引入doInBackground()而设法强制停止FlagCancelled,则会调用onPostExecute(),但onCancelled()onCancelled(Void result)都不会(因为API)级别11)未被调用。 (我不知道为什么,因为它们应该被调用而且onPostExecute()不应该,“Android API doc说:调用cancel()方法可以保证永远不会调用onPostExecute(Object)。” - IdleSunanswering a similar question)。

另一方面,如果相同的AsyncTask在取消之前没有启动doInBackground(),那么一切正常,isCancelled()更改为true,我可以在

中检查
@Override
    protected void onCancelled() {
        Log.d(TAG, String.format("mAsyncTask - onCancelled: isCancelled = %b, FlagCancelled = %b", this.isCancelled(), FlagCancelled ));
    super.onCancelled();
}

答案 4 :(得分:2)

即使AsyncTask不应该用于长时间运行的操作,有时它也可能被捕获在没有响应的任务中(例如不响应的HTTP调用)。在这种情况下,可能需要取消AsyncTask。

我们必须在这方面遇到挑战。 1.使用AsyncTask显示的常用进度对话框是用户按下后退按钮时AsyncTask上取消的第一件事。 2. AsyncTask可能在doInBackground方法中

通过在ProgressDialog上创建dismissDialogListerner,用户可以按后退按钮实际上使AsycnTask无效并关闭对话框本身。

以下是一个例子:

public void openMainLobbyDoor(String username, String password){
    if(mOpenDoorAsyncTask == null){
        mOpenDoorAsyncTask = (OpenMainDoor) new OpenMainDoor(username, password, Posts.API_URL, 
                mContext, "Please wait while I unlock the front door for you!").execute(null, null, null);
    }
}

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

    //declare needed variables
    String username, password, url, loadingMessage;
    int userValidated;
    boolean canConfigure;
    Context context;
    ProgressDialog progressDialog;

    public OpenMainDoor(String username, String password, String url, 
                Context context, String loadingMessage){
        userValidated = 0;
        this.username = username;
        this.password = password;
        this.url = url;
        this.context = context;
        this.loadingMessage = loadingMessage;
    }

    /**
     * used to cancel dialog on configuration changes
     * @param canConfigure
     */
    public void canConfigureDialog(boolean canConfigure){
        this.canConfigure = canConfigure;
    }

    @Override
    protected void onPreExecute(){
        progressDialog = new ProgressDialog(this.context);
        progressDialog.setMessage(loadingMessage);
        progressDialog.setIndeterminate(true);
        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                mOpenDoorAsyncTask.cancel(true);
            }
        });
        progressDialog.show();
        this.canConfigure = true;
    }

    @Override
    protected Void doInBackground(Void... params) {
        userValidated = Posts.authenticateNTLMUserLogin(username, password, url, context);
        while(userValidated == 0){
            if(isCancelled()){
                break;
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void unused){
        //determine if this is still attached to window
        if(canConfigure)
            progressDialog.dismiss();

        if(userValidated == 1){
            saveLoginValues(username, password, true);
            Toast.makeText(context, R.string.main_login_pass, Toast.LENGTH_SHORT).show();
        }else{
            saveLoginValues(username, password, false);
            Toast.makeText(context, R.string.main_login_fail, Toast.LENGTH_SHORT).show();
        }
        nullifyAsyncTask();
    }

    @Override
    protected void onCancelled(){
        Toast.makeText(context, "Open door request cancelled!", Toast.LENGTH_SHORT).show();
        nullifyAsyncTask();
    }
}

答案 5 :(得分:1)

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

它对我有用。