Работа с потоками через AsyncTask Печать
Добавил(а) microsin   

Класс AsyncTask позволяет правильно и просто использовать главный поток приложения, обслуживающий интерфейс пользователя (UI thread). Этот класс позволяет выполнять фоновые операции в отдельном потоке и публиковать результаты этих операций для UI thread, без всякой необходимости вручную манипулировать потоками thread и/или обработчиками (handler). Здесь приведен перевод документации [1].

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

Как понятно уже из названия, класс AsyncTask предназначен для выполнения асинхронных по отношению к UI thread задач. Асинхронная задача - это некое вычисление, которое работает в фоновом потоке, и свои результаты публикует в UI thread. Асинхронная задача определена 3 стандартными типами, которые называются Params, Progress и Result, и 4 шагами, называемыми onPreExecute, doInBackground, onProgressUpdate и onPostExecute. Дополнительную информацию по использованию процессов и потоков см. в [2].

[Как применять AsyncTask]

Для того, чтобы использовать AsyncTask, от него необходимо породить подкласс (subclass). В подклассе должен быть переопределен как минимум один метод (doInBackground(Params...)). Также часто переопределяется второй метод (onPostExecute(Result)), и иногда другие методы. Вот пример порождения подкласса на основе родительского класса AsyncTask:

private class DownloadFilesTask extends AsyncTask < URL, Integer, Long >
{
     protected Long doInBackground(URL... urls)
     {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++)
         {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Ранний выход, если был вызван cancel().
             if (isCancelled()) break;
         }
         return totalSize;
     }
 
     protected void onProgressUpdate(Integer... progress)
     {
         setProgressPercent(progress[0]);
     }
 
     protected void onPostExecute(Long result)
     {
         showDialog("Downloaded " + result + " bytes");
     }
 }

После создания выполнить задачу очень просто:

new DownloadFilesTask().execute(url1, url2, url3);

[Стандартные типы AsyncTask]

В асинхронной задаче используется три следующих типа:

1. Params, тип параметров, отправленных задаче на выполнение.
2. Progress, тип единиц прогресса, публикуемого во время фоновых вычислений.
3. Result, тип результата фоновых вычислений.

Не все эти типы используются в асинхронной задаче. Чтобы пометить, что тип не используется, используйте тип Void:

private class MyTask extends AsyncTask< Void, Void, Void > { ... }

[4 шага AsyncTask]

Когда выполняется асинхронная задача, то она проходит через 4 шага:

1. onPreExecute(), вызывается в UI thread перед тем, как задача начнет выполняться. Этот шаг обычно используется для настройки задачи, например для показывания полоски прогресса в интерфейсе пользователя.

2. doInBackground(Params...), вызывается в фоновом потоке задачи сразу после того, как завершит работу onPreExecute(). Этот шаг используется для фоновых вычислений, которые займут значительное время. На этот шаг передаются параметры асинхронной задачи. Результат вычисления должен быть возвращен на этом шаге, и он будет передан в последний шаг. Этот шаг также использует publishProgress(Progress...), чтобы опубликовать одну или большее количество единиц прогресса. Эти значения публикуются в UI thread, на шаге onProgressUpdate(Progress...).

3. onProgressUpdate(Progress...), вызывается в UI thread после вызова publishProgress(Progress...). Время выполнения не определено. Этот метод используется для отображения прогресса любой формы в интерфейсе пользователя - например для анимирования прогресс-бара, или для того, чтобы показать записи лога в текстовом поле.

4. onPostExecute(Result), вызывается в UI thread после завершения фоновых вычислений. Результат фонового вычисления передается на этом шаге как параметр.

[Отмена задачи]

Задача может быть отменена в любой момент вызовом cancel(boolean). После вызова этого метода вызов isCancelled() будет возвращать true. Также после вызова этого метода вместо onPostExecute(Object) будет вызван onCancelled(Object) после того, как завершится doInBackground(Object[]). Чтобы обеспечить максимально быстрое завершение задачи, Вы всегда должны периодически проверять значение isCancelled() из циклов doInBackground(Object[]).

[Правила работы с потоками]

Чтобы правильно работал класс AsyncTask, следует соблюдать несколько правил:

• Класс AsyncTask должен быть загружен в UI thread. С JELLY_BEAN это делается автоматически.
• Экземпляр задачи должен быть создан в UI thread.
• execute(Params...) должен быть вызван в UI thread.
• Не вызывайте onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) вручную.
• Задача может быть выполнена только один раз (при попытке повторного выполнения будет выброшено исключение (thrown exception).

[Видимость данных в памяти]

AsyncTask гарантирует, что все callback-вызовы синхронизируются таким образом, что последующие операции безопасны без явных синхронизаций.

• Установите поля экземпляра класса в конструкторе или в onPreExecute(), и обращайтесь к ним в doInBackground(Params...).
• Установите поля экземпляра класса в doInBackground(Params...), и обращайтесь к ним в onProgressUpdate(Progress...) и onPostExecute(Result).

[Порядок выполнения]

Когда AsyncTasks был представлен впервые, он выполнялся последовательно в одном фоновом потоке. Начиная с DONUT это было изменено на пул потоков, что позволило нескольким задачам выполняться параллельно. Начиная с HONEYCOMB задачи выполнятся в одном потоке, чтобы избежать общих ошибок приложения, вызываемых параллельными вычислениями.

Если Вам нужны по-настоящему параллельные вычисления, то Вы можете вызвать executeOnExecutor(java.util.concurrent.Executor, Object[]) с THREAD_POOL_EXECUTOR.

[Общий обзор методов и полей AsyncTask]

Встроенные (Nested) классы
enum AsyncTask.Status Показывает текущий статус задачи.
Поля
public static final Executor SERIAL_EXECUTOR Executor, который выполняет задачи по одной в последовательном порядке.
public static final Executor THREAD_POOL_EXECUTOR Executor, который может использоваться для выполнения задач параллельно.
Общедоступные (Public) конструкторы
AsyncTask()
Создает новую асинхронную задачу.
Общедоступные (Public) методы
final boolean cancel(boolean mayInterruptIfRunning)

Делает попытку отменить выполнение этой задачи.

static void execute(Runnablerunnable)
Удобная версия execute(Object) для использования с простым Runnable-объектом.
final AsyncTask< Params, Progress, Result > execute(Params... params)
Выполняет задачу с указанными параметрами.
final AsyncTask< Params, Progress, Result > executeOnExecutor(Executorexec, Params... params)
Выполняет задачу с указанными параметрами.
final Result get(long timeout, TimeUnitunit)
Ждет при необходимости завершения вычисления в течение самого большого указанного времени, и затем получает результат.
final Result get()
Ждет при необходимости результата вычисления, и затем получает результат.
final AsyncTask.Status getStatus()
Возвращает текущий статус этой задачи.
final boolean isCancelled()
Возвращает true, если эта задача была отменена до полного нормального завершения.
Защищенные (Protected) методы
abstract Result doInBackground(Params... params)
Переопределите этот метод, чтобы выполнять какие-то вычисления в фоновом потоке.
void onCancelled(Result result)

Запускается в UI thread после вызова cancel(boolean) и завершения doInBackground(Object[]).

void onCancelled()

Тот же самый обработчик без параметра. Приложения предпочтительно должны переопределять onCancelled(Object).

void onPostExecute(Result result)

Запускается в UI после doInBackground(Params...).

void onPreExecute()
Запускается в UI перед doInBackground(Params...).
void onProgressUpdate(Progress... values)
Runs on the UI thread after publishProgress(Progress...) is invoked.
final void publishProgress(Progress... values)
Этот метод может быть вызван из doInBackground(Params...), чтобы опубликовать данные о текущей работе и обновить данные в UI thread. Это обычно используется, чтобы определить, что фоновые вычисления еще идут, и на какой стадии они находятся (может быть перерисована, к примеру, полоска прогресс-бара).

Подробное описание полей и методов AsyncTask см. в документации [1].

[Ссылки]

1. AsyncTask site:developer.android.com.
2. Процессы и потоки.