Android AsyncTask для длительных операций

Цитируя документацию для AsyncTask, найденную здесь, в ней говорится:

В идеале AsyncTasks следует использовать для коротких операций (максимум несколько секунд). Если вам необходимо поддерживать работу потоков в течение длительного времени, настоятельно рекомендуется использовать различные API-интерфейсы, предоставляемые java.util.concurrent pacakge, такие как Исполнитель, ThreadPoolExecutor и FutureTask.

Теперь у меня возникает вопрос: почему? Функция doInBackground() запускается из потока пользовательского интерфейса, так что какой вред от длительной операции здесь?


person user1730789    schedule 09.10.2012    source источник
comment
Проблема, с которой я столкнулся при развертывании приложения (использующего asyncTask) на реальном устройстве, заключалась в том, что длительная функция doInBackground замораживает экран, если индикатор выполнения не используется.   -  person venkatKA    schedule 09.10.2012
comment
Поскольку AsyncTask привязан к Activity, в которой он запущен. Таким образом, если Activity убивается, ваш экземпляр AsyncTask также может быть убит.   -  person IgorGanapolsky    schedule 07.02.2014
comment
Что, если я создам AsyncTask внутри службы? Разве это не решило бы проблему?   -  person eddy    schedule 04.09.2014
comment
Используйте IntentService, идеальное решение для длительной работы в фоновом режиме.   -  person Keyur Thumar    schedule 01.02.2017


Ответы (4)


Это очень хороший вопрос, программисту Android требуется время, чтобы полностью разобраться в проблеме. Действительно, у AsyncTask есть две основные проблемы, которые связаны:

  • Они слабо привязаны к жизненному циклу деятельности.
  • Они очень легко создают утечки памяти.

В приложении RoboSpice Motivations (доступно в Google Play) мы подробно отвечаем на этот вопрос. Он даст подробное представление об AsyncTasks, Loaders, их функциях и недостатках, а также познакомит вас с альтернативным решением для сетевых запросов: RoboSpice. Сетевые запросы являются обычным требованием в Android и по своей природе являются длительными операциями. Вот отрывок из приложения:

Жизненный цикл AsyncTask и Activity

AsyncTasks не следуют жизненному циклу экземпляров Activity. Если вы запускаете AsyncTask внутри Activity и вращаете устройство, Activity будет уничтожено и будет создан новый экземпляр. Но AsyncTask не умрет. Он будет жить, пока не завершится.

И когда он завершится, AsyncTask не обновит пользовательский интерфейс нового Activity. На самом деле он обновляет предыдущий экземпляр активности, который больше не отображается. Это может привести к исключению типа java.lang.IllegalArgumentException: представление, не прикрепленное к диспетчеру окон, если вы используете, например, findViewById для получения представления внутри Activity.

Проблема с утечкой памяти

Очень удобно создавать AsyncTasks как внутренние классы ваших Activity. Поскольку AsyncTask необходимо будет управлять представлениями Activity, когда задача завершена или выполняется, использование внутреннего класса Activity кажется удобным: внутренние классы могут напрямую обращаться к любому полю внешнего класса.

Тем не менее, это означает, что внутренний класс будет содержать невидимую ссылку на свой экземпляр внешнего класса: Activity.

В конечном итоге это приводит к утечке памяти: если AsyncTask длится долго, он сохраняет активность «живой», тогда как Android хотел бы избавиться от нее, поскольку она больше не может отображаться. Действие не может быть собрано мусором, и это центральный механизм для Android для сохранения ресурсов на устройстве.


Это действительно очень плохая идея использовать AsyncTasks для длительных операций. Тем не менее, они подходят для короткоживущих, таких как обновление View через 1 или 2 секунды.

Я рекомендую вам загрузить приложение RoboSpice Motivations, он действительно подробно объясняет это и предоставляет образцы и демонстрации различных способов выполнения некоторых фоновых операций.

person Snicolas    schedule 26.10.2012
comment
@Snicolas Привет. У меня есть приложение, в котором оно сканирует данные из тега NFC и отправляет их на сервер. Он отлично работает в областях с хорошим сигналом, но там, где нет сигнала, AsyncTask, который заставляет веб-вызов, продолжает работать. например, диалоговое окно прогресса длится несколько минут, а затем, когда оно исчезнет, ​​экран станет черным и не отвечает. Моя AsyncTask - это внутренний класс. Я пишу обработчик для отмены задачи через X секунд. Кажется, что приложение отправляет старые данные на сервер через несколько часов после сканирования. Может ли это быть из-за того, что AsyncTask не завершается, а затем, возможно, завершается через несколько часов? Буду признателен за любое понимание. Благодарность - person turtleboy; 19.07.2013
comment
Проследите, чтобы увидеть, что происходит. Но да, это вполне возможно! Если вы хорошо спроектировали свою асинтаксическую задачу, вы можете отменить ее достаточно правильно, это будет хорошей отправной точкой, если вы не хотите переходить на RS или службу ... - person Snicolas; 19.07.2013
comment
@Snicolas Спасибо за ответ. Вчера я сделал сообщение на SO, в котором изложил мою проблему и показал код обработчика, который я написал, чтобы попытаться остановить AsyncTask через 8 секунд. Не могли бы вы взглянуть, если у вас будет время? Будет ли вызов AsyncTask.cancel (true) из обработчика правильно отменить задачу? Я знаю, что должен периодически проверять значение iscancelled () в моем doInBackgroud, но я не думаю, что это применимо к моим обстоятельствам, поскольку я просто делаю однострочный веб-вызов HttpPost и не публикую обновления в пользовательском интерфейсе. Есть ли альтернативы AsyncTask например, можно ли сделать HttPost из IntentService - person turtleboy; 19.07.2013
comment
вот ссылка stackoverflow .com / questions / 17725767 / - person turtleboy; 19.07.2013
comment
Вам следует попробовать RoboSpice (на github). ;) - person Snicolas; 19.07.2013
comment
@Snicolas ммм, на самом деле я не очень хочу добавлять библиотеки в приложение, я бы предпочел использовать исправление для AsyncTask, IntentService, возможно, вариант или некоторую чистую альтернативу потоковой передачи Java для Async. Я не игнорирую RoboSpice, но мне нужно разобраться в этом в ближайшие несколько дней, поскольку клиент моей компании отказывается! Десятки тысяч фунтов стерлингов :( Правильно ли я говорю, что невозможно принудительно отменить AsyncTask с помощью одной кнопки Cancel (true)? - person turtleboy; 19.07.2013
comment
Это то же самое, даже если я запускаю AsyncTask внутри службы? Я имею в виду, будет ли проблема с длительной работой? - person eddy; 04.09.2014
comment
@eddy, вам все равно придется иметь дело с жизненным циклом службы. Более того, было бы непросто настроить обратный вызов ... Во всяком случае, это было бы немного похоже на использование деградированной версии RS, и она может предложить гораздо больше .. - person Snicolas; 04.09.2014
comment
@Snicolas Даже не используя широковещательный приемник или LocalBroadcastManager? - person eddy; 04.09.2014
comment
Вопрос @Snicolas, что, если мы вызовем .cancel (boolean) для длительной асинхронной задачи из onPause () (до того, как произойдет ориентация), остановится ли она немедленно и предотвратит ли утечку? вопрос2, если мы создаем новый поток, нужно ли нам следить за ним, когда активность переходит в фоновый режим / уничтожается - нужно ли нам ее отменить; - person ; 02.12.2015
comment
Отмена отмены, вероятно, предотвратит утечку, хотя отмена задачи сильно зависит от ее состояния. Например, если поток находится в состоянии блокировки ввода-вывода, нет способа отменить его до того, как он вернется ... Более того, вызов cancel означает, что вы теряете данные, которые вы уже получили, + время, потраченное на установление соединения. .. RS предлагает лучшее решение. - person Snicolas; 13.01.2016
comment
Итак, если я реализую AsyncTask как обычный класс (а не внутренний класс) и сохраню свои ссылки действительными (это возможно с шаблоном слушателя / наблюдателя), могу ли я использовать AsyncTask для длительных процессов? - person Vigen; 23.10.2016

Зачем ?

Поскольку AsyncTask по умолчанию использует пул потоков , который вы не создавали. Никогда не связывайте ресурсы из пула, который вы не создавали, поскольку вы не знаете, каковы требования к этому пулу. И никогда не привязывайте ресурсы из пула, который вы не создавали, если документация для этого пула говорит вам не делать этого, как здесь.

В частности, начиная с Android 3.2, пул потоков, используемый AsyncTask по умолчанию (для приложений, для которых android:targetSdkVersion установлено значение 13 или выше), имеет только один поток в нем - если вы связали этот поток на неопределенный срок, ни одна из ваших задач не будет запущена.

person CommonsWare    schedule 09.10.2012
comment
Спасибо за это объяснение .. Я не нашел ничего действительно ошибочного в использовании AsyncTasks для длительных операций (хотя я обычно сам делегирую их службам), но ваш аргумент о привязке этого ThreadPool (для которого вы действительно можете не делайте предположений о его размере) кажется точным. В качестве дополнения к сообщению Anup об использовании службы: эта служба должна сама запускать задачу в фоновом потоке, а не блокировать свой собственный основной поток. Вариантом может быть IntentService или, для более сложных требований параллелизма, применить вашу собственную стратегию многопоточности. - person baske; 09.10.2012
comment
Это то же самое, даже если я запускаю AsyncTask внутри службы? Я имею в виду, будет ли проблема с длительной работой? - person eddy; 04.09.2014
comment
@eddy: Да, поскольку это не меняет природы пула потоков. Для Service просто используйте Thread или ThreadPoolExecutor. - person CommonsWare; 04.09.2014
comment
Спасибо @CommonsWare, последний вопрос, имеет ли TimerTasks те же недостатки, что и AsyncTasks? или это совсем другое дело? - person eddy; 04.09.2014
comment
@eddy: TimerTask - это стандартная версия Java, а не Android. От TimerTask по большей части отказались в пользу ScheduledExecutorService (который, несмотря на свое название, является частью стандартной Java). Ни один из них не привязан к Android, поэтому вам все равно нужна служба, если вы ожидаете, что эти вещи будут работать в фоновом режиме. И вам действительно следует подумать о AlarmManager от Android, так что вам не нужна служба, которая просто следит за тиканьем часов. - person CommonsWare; 04.09.2014
comment
Понятно! Большое спасибо @CommonsWare - person eddy; 04.09.2014
comment
has only one thread in it -- if you tie up this thread indefinitely, none of your other tasks will run. Ответил на мой вопрос :) - person zgc7009; 13.01.2015

Задача Aysnc - это специализированные потоки, которые по-прежнему предназначены для использования с графическим интерфейсом вашего приложения, но при этом сохраняют ресурсоемкие задачи потока пользовательского интерфейса. Поэтому, когда такие вещи, как обновление списков, изменение ваших представлений и т. Д., Требуют от вас выполнения некоторых операций выборки или операций обновления, вы должны использовать асинхронные задачи, чтобы вы могли держать эти операции вне потока пользовательского интерфейса, но обратите внимание, что эти операции все еще каким-то образом связаны с пользовательским интерфейсом. .

Для более длительных задач, которые не требуют обновления пользовательского интерфейса, вы можете вместо этого использовать службы, потому что они могут работать даже без пользовательского интерфейса.

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

для получения дополнительной информации см. темы:

AsyncTask дольше нескольких секунд?

и

AsyncTask не остановится, даже если действие было уничтожено

person Anup Cowkur    schedule 09.10.2012

Проблема с AsyncTask заключается в том, что если он определен как нестатический внутренний класс действия, он будет иметь ссылку на действие. В сценарии, когда действие контейнера асинхронной задачи завершается, но фоновая работа в AsyncTask продолжается, объект действия не будет собираться мусором, поскольку на него есть ссылка, это вызывает утечку памяти.

Решение исправить это - определить асинхронную задачу как статический внутренний класс активности и использовать слабый ссылка на контекст.

Но все же рекомендуется использовать его для простых и быстрых фоновых задач. Чтобы разработать приложение с чистым кодом, лучше использовать RxJava для запуска сложных фоновые задачи и обновление пользовательского интерфейса с их результатами.

person Arnav Rao    schedule 28.03.2018