Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AysncTask cancelling itself still calls onPostExecute()

After calling AsyncTask.cancel(true) from within doInBackground(), instead of calling onCancelled(), Android calls onPostExecute(). But as per the documentation:

Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked.

Is it a bug in Android?

More Observations:

  1. Calling cancel(false) from either thread works as specified in the documentation.
  2. Calling cancel(true) from the UI task does not call onPostExecute(), nor does it throw the InterruptedException seen in the logcat traces below.
  3. Calling cancel(false/true) from any thread sometimes calls onCancelled() even before doInBackground() returns. This is clearly in violation of the documentation, which states:

Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns.

Code: (Tested on Android 2.2 device)

protected Void doInBackground(Void... params) {
    Log.d(TAG, "started doInBackground()");
    while (!isCancelled()) {
        boolean ret = cancel(true);
        Log.d(TAG, "cancel() returned: " + ret);
    }
    Log.d(TAG, "returning from doInBackground()");
    return null;
}

Logcat output

04-15 21:38:55.519: D/MyTask(27597): started doInBackground()
04-15 21:38:55.589: W/AsyncTask(27597): java.lang.InterruptedException
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1254)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:219)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.FutureTask.get(FutureTask.java:82)
04-15 21:38:55.589: W/AsyncTask(27597):     at android.os.AsyncTask$3.done(AsyncTask.java:196)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.FutureTask$Sync.innerCancel(FutureTask.java:293)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.FutureTask.cancel(FutureTask.java:75)
04-15 21:38:55.589: W/AsyncTask(27597):     at android.os.AsyncTask.cancel(AsyncTask.java:325)
04-15 21:38:55.589: W/AsyncTask(27597):     at com.example.test.TestActivity$MyTask.doInBackground(TestActivity.java:31)
04-15 21:38:55.589: W/AsyncTask(27597):     at com.example.test.TestActivity$MyTask.doInBackground(TestActivity.java:1)
04-15 21:38:55.589: W/AsyncTask(27597):     at android.os.AsyncTask$2.call(AsyncTask.java:185)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.FutureTask.run(FutureTask.java:137)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
04-15 21:38:55.589: W/AsyncTask(27597):     at java.lang.Thread.run(Thread.java:1096)
04-15 21:38:55.589: D/MyTask(27597): cancel() returned: true
04-15 21:38:55.589: D/MyTask(27597): returning from doInBackground()
04-15 21:38:55.659: D/MyTask(27597): onPostExecute()
like image 280
Dheeraj Vepakomma Avatar asked Apr 15 '12 16:04

Dheeraj Vepakomma


2 Answers

  1. There is an exception because you call cancel(true) which sends an interrupt to the thread running doInBackground() - however, in this case, you are calling cancel(true) from within doInBackground(), thus causing the thread to immediately send an interrupt to itself.

  2. Your code is running on Android 2 but you are quoting the docs for Android 4. The problem is that the behaviour on cancel() changed between Android 2 and Android 4.

    Android 2.3.7 onPostExecute :

    Runs on the UI thread after doInBackground. The specified result is the value returned by doInBackground or null if the task was cancelled or an exception occured.

    Android 4.0.1 onPostExecute :

    Runs on the UI thread after doInBackground. The specified result is the value returned by doInBackground. This method won't be invoked if the task was cancelled.

like image 164
bain Avatar answered Oct 04 '22 22:10

bain


You should return null and treat the return in the onPostExecute.

like image 22
Natan Lotério Avatar answered Oct 04 '22 23:10

Natan Lotério