Pull to refresh

Особенность работы класса AsyncTask

Reading time2 min
Views7.1K
В данной статье разговор пойдет об одной особенности класса AsyncTask, которая с первого взгляда не бросается в глаза, но может привести к нежелательным последствиям.

Поехали...


Особенность заключается в работе метода cancel().
Если обратить внимание, то видно, что данный метод возвращает значение типа boolean и возможна такая ситуация, при которой данный метод может вернуть false. После такого «провального» вызова метода cancel(), метод isCancelled() также будет возвращать false.
Поэтому, если в вашем проекте есть нечто похожее на следующий код, то как минимум, он не всегда может работать так, как вы этого ожидаете.

private class Task extends AsyncTask<Object, Object, Object> {
     ...
     protected void onPostExecute(Object result) {
         if (!isCancelled()) {
             //do something interesting
         }
     }
     ...
}


Причины


Предположим, что AsyncTask завершает свою работу, выходит из метода doInBackground() и «постит» вызов метода onPostExecute() в главную очередь событий.
Так же допустим, что именно в этот момент пользователь выполняет некоторые действия, результатом которых становиться вызов у AsyncTask метода cancel().

В данном случае, метод onPostExecute() еще не был вызван, но отменить выполнение AsyncTask уже не возможно — метод cancel() вернет false. И, следовательно, метод isCancelled() при последующих вызовах так же будет возвращать false.

Происходит это из-за того, что внутреннее состояние AsyncTask ко времени вызова метода cancel() уже изменилось (так как управление из метода doInBackground() было возращено) и AsyncTask «думает», что уже поставленная задача уже выполнена и отменять нечего.

В результате, хотя мы и пытались отменить выполнение AsyncTask, у нас это не получилось. И поэтому, простой проверки !isCancelled(), как в примере выше не достаточно.

Что делать


Если AsyncTask описан в Activity и отменять AsyncTask необходимо только при выходе из Activity, то можно так же проверять возвращаемое значение метода isFinishing.
Но данный способ не особо универсален.
Вариант получше — это написать свой класс (скажем, AbsAsyncTask) и переопределить методы cancel() и isCancelled(), но в SDK они объявлены как final.
Поэтому, в нашем проекте используется класс-обертка, который оборачивает все вызовы методов AsyncTask и при вызове метода cancel() взводится специальный флаг, который не зависимо от внутреннего состояния AsyncTask позволяет точно узнать вызывался ли метод cancel() или нет.
Tags:
Hubs:
Total votes 30: ↑29 and ↓1+28
Comments6

Articles