ISSN 1991-3087
Рейтинг@Mail.ru Rambler's Top100
Яндекс.Метрика

НА ГЛАВНУЮ

Использование и оптимизация шаблона асинхронных задач в операционной системе Android

 

Дроздов Артем Андреевич,

соискатель Национального исследовательского Томского политехнического университета.

 

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

Операционная система Android содержит в себе встроенный механизм работы с асинхронными задачами, называемый AsyncTask [1, с.237]. Фактически он представляет собой паттерн проектирования, называемый шаблонный метод. Для его использования необходимо создать класс-наследник и определить в нем метод doInBackground, в рамках которого будет выполнена длительная операция. Дополнительно можно переопределить методы onPreExecute и onPostExecute. Первый служит для того, чтобы произвести инициализацию данных, необходимых для выполнения основной операции. Данная инициализация производится в главном потоке. Второй метод позволяет передать результат выполнения операции в главный поток. Совокупность этих методов позволяет определить семантически самостоятельную единицу кода – задачу.

У этого подхода присутствует один недостаток: невозможно контролировать число одновременно выполняемых операций. Согласно исходному коду операционной системы Android можно увидеть, что в рамках одного приложения может одновременно выполнятся до 128 одновременных операций. Еще 10 задач при этом могут находиться в очереди. В случае превышения этого числа, будет выброшено исключение, которое, скорее всего, приведет к завершению приложения с ошибкой. Помимо вероятности аварийного завершения существует также проблема с отзывчивостью пользовательского интерфейса. Дело в том, что каждый запускаемый поток обладает тем же приоритетом исполнения, что и главный. Таким образом, если одновременно выполняется 10 потоков, то главный поток будет работать в 10 раз медленнее (в случае одноядерной архитектуры процессора). Еще одной характерной особенностью такого подхода является увеличенный расход оперативной памяти, являющейся наиболее дефицитным ресурсом для платформы Android. Очевидно, что выполнение задачи требует определенного расхода памяти. Соответственно, при параллельном исполнении задач расход памяти суммируется, что при невозможности управления количеством одновременно выполняемых задач ведет к практически неустранимой ошибке OutOfMemoryError.

Для разрешения указанных проблем в высоконагруженных системах часто используется очередь задач и определенное число потоков, исполняющих эти задачи [2, с.490]. В рамках операционной системы Android использование этого подхода без модификации не является оправданным, т.к. большинство задач необходимо исполнять частично в отдельном потоке, для обеспечения отзывчивости пользовательского интерфейса во время длительных операций, частично в главном потоке – для отображения результатов пользователю. Чтобы обеспечить такое поведение предлагается следующий шаблонный метод, схема которого изображена на рисунке 1.

 

Рис. 1. Схема шаблонного метода.

 

В виде псевдокода данный метод можно представить следующим образом:

 

class Task{

  //Хэндлер для передачи обработки в главный поток

  static Handler handler = new Handler(Looper.getMainLooper().getThread());

  void run(){

    try {

      async();

      handler.post(() -> { ok(); });

    } catch (Throwable t) {

      handler.post(() -> { error(t); });

    } finally {

      handler.post(() -> { end(); });

    }

  }

 

  void async();

  void ok();

  void error(Throwable t);

  void end();

}

 

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

Для непосредственного выполнения задач рекомендуется использовать класс ThreadPoolExecutor с неограниченной очередью задач. Возможно использование различных ThreadPoolExutor’ов с целью более тонкой настройки распределения нагрузки.

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

 

Литература

 

1.                  Голощапов А. Google Android: программирование для мобильных устройств. — СПб.: БХВ-Петербург, 2010. — 448 с.

2.                  Кей Хорстманн . Java 2. Библиотека профессионала. Том 2. Тонкости программирования / Кей Хорстманн, Гари Корнелл — М.: Вильямс, 2010 г. – 992 с.

3.                  П. Дейтел. Android для программистов. Создаем приложения / П. Дейтел, Х. Дейтел, Э. Дейтел, М. Морган – СПб.: Питер, 2012г. – 691 c.

 

Поступила в редакцию 10.12.2012 г.

2006-2019 © Журнал научных публикаций аспирантов и докторантов.
Все материалы, размещенные на данном сайте, охраняются авторским правом. При использовании материалов сайта активная ссылка на первоисточник обязательна.