Android AsyncTask won't stop when cancelled, why?

Solution 1:

Keep in mind that your Activity and your AsyncTask are two separate threads. So even if you cancel the AsyncTask, the code to cancel the task may not run yet! That's why you are seeing the AsyncTask still running even after fixing your bug. I don't think the status will change until doInBackground() in your AsyncTask has completed. (So make sure you're checking isCancelled() and returning early when you've been cancelled!)

Solution 2:

I dug deeper and remembered that my thread was calling a rather badly written method containing this, producing another thread inside the one I need to cancel:

public static void haveASleep(int milliseconds)
{
    try
    {
        Thread.sleep(milliseconds);
    }
    catch (InterruptedException ie)
    {
        Trace.w(TAG,"Sleep interrupted");
    }
}

So what was happening was that the AsyncTask thread calls this method to wait a while, and during the sleep the cancel() method call occurs on the AsyncTask, which causes an exception in the thread sleep. My code unhelpfully caught and absorbed that exception, instead of using it to shut down the AsyncTask.

The solution was to look for an exception on the sleep, and if it is thrown, to quietly exit the thread. The app now works as expected, BUT...

The status immediately after the cancel() method call remains RUNNING, but I suspect this is because at that moment it is still running, and the OS has not yet shut it down. I have no idea if that's true, but that's my best estimate.

Solution 3:

thread.cancel(true);

Only sets a variable in the AsyncTask class. What you need to do is implement a loop into your background task if you haven't already. Then check and break the loop if cancel is true for each iteration.

@Override  
protected Void doInBackground(Void... params) {  

    synchronized(this) {

        for(int i = 0; i < 9000; i++) {

            if(thread.isCancelled()) break;

            // do stuff

        }

    }

}

Then just set it to cancelled whenever you need it to stop. It will then stop before the following iteration.

@Override
public void onStop() {

    super.onStop();

    thread.cancel(true);

}

@Override
public void onDestroy() {

    super.onDestroy();

    thread.cancel(true);

}