AzoftБлогНастройка связи между компонентами Android при помощи шины событий

Настройка связи между компонентами Android при помощи шины событий

Алексей Васильков Сентябрь 27, 2013

Изображение Julián Toledo

При разработке Android-приложений часто возникает необходимость наладить взаимодействие между различными компонентами системы, такими как Activity, Fragment, Service и AsyncTask, т. к. обычно эти компоненты слабо связаны друг с другом и имеют собственные жизненные циклы, что значительно затрудняет коммуникацию.

Недостатки обычного подхода

Стандартный подход к организации взаимодействия компонентов заключается в создании специальных интерфейсов — «листенеров» (listener). Любой желающий может передать реализацию такого интерфейса соответствующему компоненту, который будет самостоятельно уведомлять о происходящих в нем событиях, используя переданный ему листенер.

Однако, компоненты не всегда могут знать о существовании друг друга и передавать листенеры напрямую. Например, если пользователь на одном из экранов изменил какие-то данные (изменил свое имя), то на других, уже открытых экранах, нам также необходимо обновить эти данные. Но активити (как и фрагменты) в Android не имеют прямого доступа друг к другу, поэтому передача листенера от одной активити к другой затруднена.

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

Преимущества использования шины событий

В этой ситуации на помощь приходит достаточно хорошо себя зарекомендовавший подход с использованием шины событий (шаблон publish-subscribe). Этот подход широко использует сам Android, например в механизме интентов.

Суть подхода состоит в следующем: компоненты, которые хотят получать уведомления о каких-либо событиях, происходящих в других частях программы, регистрируются в общей шине («подписываются» на события). При возникновении события в шину посылается («публикуется») соответствующее уведомление, которое будет получено всеми зарегистрированными подписчиками. Таким образом, компонентам больше не требуется знать о существовании друг друга.

Sticky-события

В шине также могут быть опубликованы так называемые sticky-события — события, которые постоянно находятся в шине и доставляются всем новым подписчикам (а также всем подписчикам на момент публикации). Если при публикации нового sticky-события событие такого же типа уже находится в шине, то оно будет заменено новым. 

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

Примеры использования шины событий в приложении

1. Загрузка и отображение новостей

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

Здесь мы можем выделить четыре компонента: меню, экран списка новостей, локальная база данных (БД) и задача (AsyncTask), используемая для загрузки новостей с сервера и кэширования их в БД.

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

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

2. Статус загрузки файла

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

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

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

Способ реализации

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

public class MyActivity extends Activity {

    private MyReceiver mReceiver;

    onResume() {
        …
        IntentFilter filter = new IntentFilter("com.example.MyEvent");
        mReceiver = new MyReceiver();
        registerReceiver(receiver, filter);
    }

    onPause() {
        …
        unRegisterReceiver(mReceiver);
    }

    public class MyReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "MyEvent", Toast.LENGTH_LONG).show();
        }
    }

}

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

public void sendBroadcast() {
    Intent intent = new Intent("com.example.MyEvent");
    intent.putExtra("myParam", ”Hello”);
    sendBroadcast(intent);
}

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

  • FluffyEvents, разработчик Azoft, библиотека основана на BroadcastReceiver;
  • Otto by Square, разработчик Guava;
  • EventBus, разработчик greenrobot;
  • EventBus,  разработчик Richard Taylor.

Комментарии

комментарии



Content created by Alex Vasilkov