В данной статье разговор пойдет об одной особенности класса AsyncTask, которая с первого взгляда не бросается в глаза, но может привести к нежелательным последствиям.
Особенность заключается в работе метода
Если обратить внимание, то видно, что данный метод возвращает значение типа
Поэтому, если в вашем проекте есть нечто похожее на следующий код, то как минимум, он не всегда может работать так, как вы этого ожидаете.
Предположим, что AsyncTask завершает свою работу, выходит из метода
Так же допустим, что именно в этот момент пользователь выполняет некоторые действия, результатом которых становиться вызов у AsyncTask метода
В данном случае, метод
Происходит это из-за того, что внутреннее состояние AsyncTask ко времени вызова метода
В результате, хотя мы и пытались отменить выполнение AsyncTask, у нас это не получилось. И поэтому, простой проверки
Если AsyncTask описан в Activity и отменять AsyncTask необходимо только при выходе из Activity, то можно так же проверять возвращаемое значение метода isFinishing.
Но данный способ не особо универсален.
Вариант получше — это написать свой класс (скажем, AbsAsyncTask) и переопределить методы
Поэтому, в нашем проекте используется класс-обертка, который оборачивает все вызовы методов 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()
или нет.