16

I've an AsyncTask that I shut down in the Activity's onPause lifecycle event, so it doesn't run when someone leaves the app, but it keeps going despite this. I added some tracing and this snippet shows the problem.

    Trace.d(TAG,"Task state: " + myTask.getStatus() );
    myTask.cancel(true);
    Trace.d(TAG,"Task state: " + myTask.getStatus() );

Outputs:

Task state: RUNNING
Task state: RUNNING

Why is the cancel() method not having any effect on the state of the task? I notice the docs say the cancel method will "attempt" to stop the task, but under what circumstances will it fail? The task is definitely running as it is outputting log output every ten seconds, and as you can see above its status is returned as running.

Update: I added tracing to show me the isCancelled() state as well and that DOES change. So the call to cancel(true) is changing the cancelled state from false to true, but apparently having no effect on the Status, or stopping the thread.

Ollie C
  • 28,313
  • 34
  • 134
  • 217
  • Is there a way for the loop inside your task to check whether the task has been cancelled, and simply exit the loop? – Joel Mueller Feb 10 '11 at 17:34
  • What is the return value from cancel? Did it tell you it did cancel it? – Cheryl Simon Feb 10 '11 at 17:36
  • @Joel Yes, I can do that, but as I use the cancel() method in other places, I want to understand why it is not doing what I expect it to – Ollie C Feb 10 '11 at 17:42
  • @Mayra The cancel() method returns true, indicating the task HAS been cancelled - this is not true, the task continues to give log output every ten seconds after the call to cancel. – Ollie C Feb 10 '11 at 17:44
  • I'm sure that who's not scared of going deep into sources (I am!) could find the answer. Status is set to `FINISHED` only when `finish()` gets called, and that happens in an internal handler used by a `FutureTask`. `cancel()` and `isCancelled()` are actually called on that `FutureTask` member. If that task is cancelled `onCancelled()` is called. Could that be used for tests? sources: http://www.google.com/codesearch/p?hl=en#uX1GffpyOZk/core/java/android/os/AsyncTask.java&d=3 – bigstones Feb 10 '11 at 18:14

5 Answers5

3

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!)

idbrii
  • 10,975
  • 5
  • 66
  • 107
2
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);

}
tpbapp
  • 2,506
  • 2
  • 19
  • 21
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.

Ollie C
  • 28,313
  • 34
  • 134
  • 217
0

Another possible pitfall to check if you're running into this problem:

Make sure you are not starting two AsyncTasks.

blub
  • 8,757
  • 4
  • 27
  • 38
0

Even if you know how cancel works it's very hard to determine in what state your thread will be stopped. So I suggest to implement your own safe way to stop your thread, so you'll be sure in what state you are stopping and you be sure there are no memory leaks, etc. From more info how to stop a thread in java in safe way check this thread How to abort a thread in a fast and clean way in java?

Community
  • 1
  • 1
Mojo Risin
  • 8,136
  • 5
  • 45
  • 58
  • I'm using AsyncTask which is an Android wrapper around the Java thread handling to, in theory at least, make it easy to start, manage, and stop threads. At the moment I suspect I will end up having to brute-force stop the thread, but I want to understand why the cancel() method isn't doing what it appears to have been designed to do. – Ollie C Feb 10 '11 at 16:47
  • AsyncTask is not just a Android wrapper. Its main advantage over Java thread is that you can easy update you gui from it using methods that are executed in the UI thread. So AsyncTask has the same problem with stopping as Java thread. – Mojo Risin Feb 11 '11 at 13:48