2012-04-15 9 views
7

Po wywołaniu AsyncTask.cancel(true) od doInBackground(), zamiast wywoływania onCancelled(), połączenia Android onPostExecute(). Ale as per the documentation:AysncTask anulowanie się wciąż wywołuje onPostExecute()

Wywołanie tej metody spowoduje onCancelled(Object) powołano na wątku UI po doInBackground(Object[]) zwrotów. Wywołanie tej metody polega na tym, że onPostExecute(Object) nigdy nie jest wywoływana.

Czy to błąd w systemie Android?

More Obserwacje:

  1. Wywołanie cancel(false) z działa albo wątek, jak określono w dokumentacji .
  2. Wywołanie cancel(true) od zadania UI robi nie połączenia onPostExecute(), ani nie rzucać InterruptedException widać w LogCat ślady poniżej.
  3. Wywołanie cancel(false/true) z dowolnego wątku czasami wywołuje onCancelled() jeszcze przed zwróceniem doInBackground(). Jest to wyraźnie sprzeczne z dokumentacją, which states:

Wywołanie tej metody spowoduje onCancelled (Object) jest wywoływana na wątku UI po doInBackground(Object[]) zwrotów.

Kod: (testowane na urządzeniu z Androidem 2.2)

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; 
} 

wyjście Logcat

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() 
+1

Dlaczego chcesz, aby anulować 'AsyncTask' od wewnątrz' doInBackground'? To nie ma sensu. Metoda 'cancel (...)' jest celowo przeznaczona do zatrzymania wykonywania kodu poza wątkiem roboczym (innymi słowy w wątku UI). Jeśli kod na 'doInBackground' musi się zakończyć z jakiegokolwiek powodu, powinien po prostu" return ". Jeśli nie chcesz, aby 'onPostExecute (...)' wykonywał określone akcje w wyniku pseudo-anulowania, zwróć 'false' w przeciwnym wypadku zwróć' true' – Squonk

+0

@MisterSquonk, "Metoda anulowania (...) jest celowo ma na celu zezwolenie na kod znajdujący się poza wątkiem roboczym w celu zatrzymania egzekucji. " Dokumentacja nie mówi, że należy ją wywoływać tylko z wątku interfejsu użytkownika. Dlaczego nie użyć ponownie istniejącego kodu 'onCancelled()' zamiast używać nieestetycznych obejść? –

+1

Zgoda, dokumentacja nie mówi, że powinna być wywołana tylko z wątku UI, ale wydaje się to najbardziej logiczne. Powodem, dla którego mówię, że dokumentacja mówi, że 'doInBackground' powinien okresowo sprawdzać' isCancelled() ', aby sprawdzić, czy musi zatrzymać swoje własne wykonanie (w wyniku wywołania' cancel '). Jeśli wywołanie 'doInBackground' wywołanie' cancel 'jest normalne, to jaki byłby punkt 'isCancelled()'? – Squonk

Odpowiedz

3
  1. Istnieje wyjątek bo nazywasz anulować (true), który wysyła przerwania do wątku uruchomiony doInBackground() - jednak w tym przypadku, dzwonisz anulować (true) od wewnątrz doInBackground(), a tym samym powodując, że wątek natychmiast wysyła do siebie przerwanie.

  2. Twój kod działa na Androidzie 2, ale jesteś powołując docs dla Androida 4. Problem polega na tym, że zachowanie na cancel() zmieniło między Androida 2 i Android 4.

    Android 2.3.7 onPostExecute:

    Działa na wątku UI po doInBackground. Podany wynik to wartość zwrócona przez doInBackground lub wartość null, jeśli zadanie zostało anulowane lub wystąpił wyjątek.

    Android 4.0.1 onPostExecute:

    działającego w wątku UI po doInBackground. Podany wynik to wartość zwrócona przez doInBackground. Ta metoda nie zostanie wywołana, jeśli zadanie zostało anulowane.

1

Należy zwrócić nieważną i traktują powrót w onPostExecute.

Powiązane problemy