Современные технологии управления фоновыми потоками в приложении Android | Статья в журнале «Молодой ученый»

Отправьте статью сегодня! Журнал выйдет 23 ноября, печатный экземпляр отправим 27 ноября.

Опубликовать статью в журнале

Автор:

Рубрика: Информационные технологии

Опубликовано в Молодой учёный №23 (157) июнь 2017 г.

Дата публикации: 12.06.2017

Статья просмотрена: 303 раза

Библиографическое описание:

Конобеева, Е. А. Современные технологии управления фоновыми потоками в приложении Android / Е. А. Конобеева. — Текст : непосредственный // Молодой ученый. — 2017. — № 23 (157). — С. 105-109. — URL: https://moluch.ru/archive/157/44439/ (дата обращения: 15.11.2024).



При запуске приложения ОС Android создает отдельный процесс для него, выделяя необходимое количество памяти. В пределах процесса приложение самостоятельно управляет потоками, разделяющими общее пространство памяти процесса. Так как под приложение отведен только один процесс, то потоки программы выполняются поочередно, но благодаря быстрому переключению процессора создается иллюзия параллельного выполнения задач. При нехватке памяти в системе, ОС Android может завершить процесс приложения для освобождения ресурсов. Системой выбирается наименее важные процессы для уничтожения, например, если приложение запущено, но не показано в данный момент на экране телефона. Одновременно с уничтожением процесса, состояние приложения будет временно сохранено в системе для того, чтобы быстро суметь восстановить приложение при его повторном запуске. Процессом сохранения состояния при неожиданном завершении приложения и дальнейшими действиями, например, повторить запуск приложения, можно управлять из класса, унаследованного от Application. На рис. 1 показана диаграмма процесса приложения с созданными потоками:

C:\Users\Evgenia\Desktop\ВКР 3\скрины\4e6d2fa4308be69a70d2c24a59750589.png

Рис. 1. Диаграмма процесса приложения с созданными потоками

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

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

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

Существование UI-потока на протяжении всего жизненного цикла приложения обеспечивается созданием бесконечного цикла — Looper, очередью сообщений Message queue и объектом Handler.

Looper — цикл обработки сообщений, который берет из очереди Message queue одно сообщение, представляющее собой набор задач необходимых для выполнения в потоке. Объект Handler представляет собой посредника между очередью сообщений и другими потоками. Он позволяет добавлять сообщение в очередь текущего потока из других потоков.

Таким образом, главный поток состоит из:

– очереди сообщений Message queue, которые должны выполниться на нем;

– бесконечного цикла Looper, который проверят очередь сообщений на наличие новой задачи;

– объекта Handler, с помощью которого осуществляется взаимодействие с очередью поток, появляется возможность добавить новую задачу для выполнения в конкретном потоке.

На рис 2 изображена схема взаимодействия трех объектов.

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

https://lh3.googleusercontent.com/I3dbWIbigsWHKXxzciqZjNSD5DMq_mFSPVEm3uOxT7LRDwCW11un298RMczFhl8sxHFe1lC9q7kdUNJjAasdT50_j1u5XirFmZ8XUscsUxoyQQmoWnsRQx5SYcPb2DJ7HCYlIFqpdZQ

Рис. 2. Схема взаимодействия Handler, Looper, Message queue

При работе с фоновым потоком важно всегда продумывать его взаимодействие с главным потоком, для отображения данных на экране мобильного приложения. Android API предоставляет несколько инструментов организации такого взаимодействия:

– AsynkTask;

– AsynkTaskLoader;

– Sirvice;

– Broadcast Reciever.

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

У перечисленных классов имеется ряд недостатков, которые усложняют их использование в проекте:

– сложная обработка ошибок;

– связность кода;

– сложная реализация выполнения нескольких запросов.

В настоящее время, так как ОС Android является открытой программной платформой, то существуют дополнительные средства управлением многопоточностью от других компаний.

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

Фреймворк RxJava предназначен для воплощения реактивного подхода к программированию на языке java. В таком подходе все данные представлены в виде потоков данных, которые можно изменять с течением времени и состояния которых могут отслеживаться «подписчиками» на поток. RxAndroid — расширение RxJava для системы Android. RxAndroid предоставляет функции для преобразования потоков данных, решает проблему возникновения исключений в процессе изменения данных, предоставляет удобный способ управления потоками в приложении.

Работа RxAndroid построена на использовании объектов Observable, которые создают потоки данных, и Subscription — подписчики, которые получают данные [1].

Для выполнения операций над данными на разных потоках в RxAndroid существует класс AndroidSchedulers, предоставляющий возможность планирования потоков. Например, после вызова функции observeOn(AndroidSchedulers.mainThread()) применительно к Observable все последующие операции над цепочкой данных будут выполняться на главном потоке.

Создать поток данных можно функцией just(Item …) [3]:

Observable observable = Observable.just(1, 2, 3);

В примере в угловых скобках указан тип данных, которые испускает Observable как единый поток. Функция just(Item …) создает Observable, который испускает перечисленные в параметрах данные по очереди, как только на источник будет подписан хотя бы один подписчик. По окончании потока данных Observable завершит свою работу, уведомляя соответствующим вызовом метода подписчика.

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

Subscription — объект, который создаётся при подписывании на объект Observable с помощью вызова метода subscribe(). Он хранит в себе всю информацию о подписке, позволяет отменять ее. Подписываться могут объекты расширяющие интерфейс Subscriber. В интерфейсе объявлены методы, которые будут вызываться источником данных в зависимости от совершаемого действия:

– Subscriber.onNext() вызывается в ответ на испускание каждого элемента;

 Subscriber.onComplete() вызывается при успешном завершении излучения потока данных;

 Subscriber.onError() вызывается при возникновении ошибки выполнения в каком-либо операторе на каком-либо потоке;

Таким образом организовать выполнение длительной операции в фоновом потоке можно несколькими строками кода:

Observable observable = Observable.just(Param param)

.map(param -> Data.comments().getComments(param))

.map(CommentsMapper::mapJsonToComment);

В приведенном фрагменте создается поток данных из одного элемента — объект типа Param. С помощью оператора map() к элементу применяется функция, возвращаемая объект другого типа. В примере возвращаемый объект типа Json. Именно в этой функции можно реализовать выполнение длительного действия, например, отправить запрос на сервер и дождаться результата. Следующий оператор map() применяет к полученному Json-объекту функцию по конвертированию его в объект Comment.java который в итоге будет получен подписчиком.

Чтобы получить результат необходимо подписаться на источник:

Subscription subscription = observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Subscriber>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {


e.printStackTrace();
showException();
unsubscribe();
}

@Override
public void onNext(ArrayList modelComments) {
showComments(modelComments, sort);

}
});

В данном фрагменте кода мы указали, в каком потоке должна выполняться подписка и в каком потоке мы должны получить результат с помощью функций:

 observable.subscribeOn(Schedulers.newThread()) — которая указывает, в каком потоке будут выполняться все операторы начиная с создания объекта observable;

 .observeOn(AndroidSchedulers.mainThread()) — указывает, что следующие после нее операторы необходимо выполнить на главном потоке.

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

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

Чтобы начать использовать описанный Фреймворк необходимо подключить следующие библиотеки [2]:

compile 'io.reactivex.rxjava2:rxjava:2.0.6'

Для использования RxAndroid :

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

Литература:

  1. Грокаем* RxJava, часть первая: основы // habrahabr. URL: https://habrahabr.ru/post/265269/ (дата обращения: 9.06.2017).
  2. Migration From RxJava 1.0 to RxJava 2.0 // github. URL: https://github.com/amitshekhariitbhu/RxJava2-Android-Samples (дата обращения: 9.06.2017).
  3. Introduction to ReactiveX // ReactiveX. URL: http://reactivex.io/intro.html (дата обращения: 9.06.2017).
Основные термины (генерируются автоматически): главный поток, поток, очередь сообщений, пользовательский интерфейс, фоновый поток, какой поток, поток данных, приложение, функция, бесконечный цикл.


Задать вопрос