Android - Cancel AsyncTask Forcefully
Asked Answered
A

6

64

I have implemented AsyncTask in my one of activity:

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

Now, i need to implement the "Cancel" button functionality, so i have to stop the execution of the running task. I don't know how do i stop the running task(background task).

So Please suggest me, how do i cancel the AsyncTask forcefully ?

Update:

I found about the Cancel() method of the same, but i found that calling cancel(boolean mayInterruptIfRunning) doesn't necessarily stop the execution of the background process. All that seems to happen is that the AsyncTask will execute onCancelled(), and won't run onPostExecute() when it completes.

Abidjan answered 20/1, 2011 at 15:26 Comment(1)
check this for a good example on correct way to cancel an asynctask quicktips.in/correct-way-to-cancel-an-asynctask-in-androidCribwork
G
91

Just check isCancelled() once in a while:

 protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled()) break;
    }
    return null;
 }
Garver answered 22/1, 2011 at 22:18 Comment(5)
@user456046 Ya you have indicated the exact thing which i am missing to add in my code. Actually, i have implemented cancel() method, but doesnt know this kind of indication we needs write inside the doInBackground() method. Thanx a lotAbidjan
@PareshMayani: From where i should invoke asynctask.cancel method,i.e.am i suppose to put my cancel button inside any of 5 methods described by asynctaskAnguiano
@PareshMayani Hey please check over there,I have explained my problem.Thank u.......Anguiano
@PareshMayani isCancelled() always returns false even i call asynctask.cancel(true);Kessiah
Add isCancelled() at begin or end of doinBackground, or anywhere?Areta
A
49

Call cancel() on the AsyncTask. Whether or not this will actually cancel anything is dependent a bit upon what you are doing. To quote Romain Guy:

If you call cancel(true), an interrupt will be sent to the background thread, which may help interruptible tasks. Otherwise, you should simply make sure to check isCancelled() regularly in your doInBackground() method. You can see examples of this at code.google.com/p/shelves.

Avenge answered 20/1, 2011 at 15:31 Comment(8)
but even after Calling Cancel() method, task is running in background and onPostExecute() method is not being executed.Abidjan
Thanx, you are absolutely right and you have also indicated the exact thing in highlighted description buat at that time i couldnt come to understand the thing, but now i come to understand your highlighted description in answer. Thanx +1 UpvoteAbidjan
Calling Cancel(true) calls onCancelled(Object) instead of onPostExecute().Rotl
Every time you add every thing you know about the answer , thanks !Adorno
A useful take-away from Romain's quote (and the docs) is that the thread is interrupted. Thus, if you are doing a long, looping operation but don't have the asynctask reference to call isCancelled() on, you can call Thread.currentThread.isInterrupted instead.Splasher
Regarding: an interrupt will be sent to the background thread, which may help interruptible tasks. I am just wondering, what are some examples of non-interruptible tasks? As doInBackground() runs on a different thread, that thread should be interruptable in any case. Doesn't this mean then that calling cancel(true) should always interrupt the current running thread? I am asking this because there are a lot of examples where one is adviced to check with isCanceled() whether the AsyncTask was stopped or not, but if cancel(true) interrupts the current thread why would we need this?Geelong
@AndyRes: See the Java documentation, along with this and this. "that thread should be interruptable in any case" -- no.Avenge
@Avenge thank you very much for the useful provided resources. Now it is clear.Geelong
J
16

It really depends on what you are doing in your asynctask.

If it's a loop processing a lot of files, you can just check after each files if the isCanceled() flag is raised or not and then break from your loop if it is.

If it's a one line command that performs a very long operation, there's not much you can do.

The best workaround would be to not use the cancel method of the asynctask and use your own cancelFlag boolean. You can then test this cancelFlag in your postExecute to decide what to do with the result.

Jacobite answered 20/1, 2011 at 16:26 Comment(1)
Thanx for the response with in-depth description, but when i was trying to implement the thing, at that time i read your answer but didnt get the exact idea, this time i have implemented the thing and AsyncTask is working and stopping successfully. Thanx for the helpful answer.Abidjan
M
5

The mentioned in comments case that isCancelled() always returns false even i call asynctask.cancel(true); is especially harmful if I close my app, but the AsyncTask continues working.

To solve this I modified the proposed by Jacob Nordfalk code in the following way:

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

and added the following to the main activity:

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

As my AsyncTask was a private class of one of views, so getters or setters of the flag were necessary to inform the AsyncTask about the currently actual flag value.

My multiple tests (AVD Android 4.2.2, Api 17) have shown that if an AsyncTask is already executing its doInBackground, then isCancelled() reacts in no way (i.e. continues to be false) to any attempts to cancel it, e.g. during mViewGroup.removeAllViews(); or during an OnDestroy of the MainActivity, each of which leads to detaching of views

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

If I manage to force stopping the doInBackground() thanks to the introduced FlagCancelled, then onPostExecute() is called, but neither onCancelled() nor onCancelled(Void result) (since API level 11) are not invoked. (I have no idea why, cause they should be invoked and onPostExecute() should not, "Android API doc says:Calling the cancel() method guarantees that onPostExecute(Object) is never invoked." - IdleSun, answering a similar question).

On the other hand, if the same AsyncTask hadn't started its doInBackground() before cancelling, then everything is ok, isCancelled() changes to true and I may check that in

@Override
    protected void onCancelled() {
        Log.d(TAG, String.format("mAsyncTask - onCancelled: isCancelled = %b, FlagCancelled = %b", this.isCancelled(), FlagCancelled ));
    super.onCancelled();
}
Morocco answered 2/11, 2013 at 20:15 Comment(0)
S
2

Even though an AsyncTask should not be used for long running operations, sometimes it may be caught in a task that does not respond (such as a non-responding HTTP call). In that case, it may be necessary to cancel the AsyncTask.

We have to challenges in doing this. 1. The usual progress dialog displayed with an AsyncTask is the first thing cancelled on an AsyncTask when the back button is pressed by the user. 2. The AsyncTask may be in the doInBackground method

By creating a dismissDialogListerner on the ProgressDialog, a user can press the back button and actually nullify the AsycnTask and close the dialog itself.

Here is an example:

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();
    }
}
Skirt answered 26/11, 2013 at 22:26 Comment(2)
what's the content of this method "nullifyAsyncTask()"Flame
nullifyAsyncTask() is a helper method to delete any async task registered to the activity. This is useful to remove any association to the async task after the application closes or goes to another activity.Skirt
R
1

Our global AsyncTask class variable

LongOperation LongOperationOdeme = new LongOperation();

And KEYCODE_BACK action which interrupt AsyncTask

   @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.

Rives answered 11/2, 2013 at 10:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.