Для чего нужна библиотека EventBus или её аналог Otto?

Rostislav Dugin

Просмотрел много ссылок в Google, но все равно не получил конкретного ответа: какую задачу решает библиотека Greenrobot EventBus (или её аналоги — Square Otto), чем она лучше стандартных вариантов и насколько она оправдывает свое использование?

Последний вопрос возникает из-за того, что я не представляю такую архитектуру, построенную на событиях. В MVP\RxJava библиотека совсем не вписывается (как минимум, у себя в проектах я не видел мест для их использования), разве что имеет смысл использовать библиотеку в общении между Service и другими частями приложения.

2 ответа

Rostislav Dugin

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

Сначала создается класс-модель с конструкторами инициализации, которая будет хранить в себе передаваемые данные:

public class MessageEvent {
    public final String message;

    public MessageEvent(String message) {
        this.message = message;
    }
}

здесь класс-модель MessageEvent с одним полем данных типа String и конструктор инициализации этих данных. Вы можете создать произвольное количество полей данных произвольных объектов, а конструкторы инициализации могут включать несколько аргументов.

Затем в классе (например активити) из которого необходимо передать наши данные создается событие:

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

В классе, куда мы желаем отправить наши данные (например фрагмент) мы регистрируем приемник события:

@Override
    public void onStart() {
        super.onStart();
        // регистрация приемника при старте фрагмента
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        // отписываемся от регистрации при закрытии фрагмента
        EventBus.getDefault().unregister(this);
        super.onStop();
    }

    // В этом методе-колбэке мы получаем наши данные 
    // (объект `event` типа класса-модели MessageEvent)
    public void onEvent(MessageEvent event){
        // извлекаем из модели отправленную строку: event.message = "Hello everyone!"
        Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
    }

Как только в активити будет выполнен код отправки события в колбэке onEvent() фрагмента появятся отправленные данные. Получат эти данные только те приемники, у которых в колбэке совпадет сигнатура с отправленным событием. Помимо классов-моделей можно передавать и простые типы данных, например:

Отправка:

int num = 5;
 EventBus.getDefault().post(num);

Получение (регистрация приемника опущена):

public void onEvent(int event){
            Toast.makeText(getActivity(), "Получено число:" + event, Toast.LENGTH_SHORT).show();
    }

Вообще, не важно, что вы шлете по шине: классы-модели сложной структуры, объекты, коллекции , массивы или примитивные типы - все это будет в целости доставлено получателю.

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

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

Так же есть другие библиотеки аналогичной функциональности, например Square Otto. Кроме того, шагнувшая намного дальше концепция реактивного программирования - фреймворк rxJava и дополнения для Android - фреймворк rxAndroid, учитывающий специфику платформы.


Rostislav Dugin

Для декомпозиции кода. Допустим у вас активити + несколько фрагментов, без "шины" вы из активити прокидываете калбеки, либо внути фрагмента кастите к классу активити (или интерфейсу, который реализует эта активити) и вызываете методы. С шиной вы в активити подписываетесь на события и из фрагментов постите события. Код про колбеки уходит, меньше кода - понятнее. Интерфейсы "развязаны" фрагменты легче переиспользовать в других активити. Либо другой пример с бизнес-логикой, пишем как отдельный модуль, который ничего не знает об UI и общается с ним через event bus. Меньше зависимостей - понятнее код, при тестировании подменяем UI тестовым кодом, который постит/подписывается на события связанные с бизнес-логикой. Т.е. все это не сделает ваш код быстрее, задачу вы не посчитаете лучше. Все это скорее вопрос "веры", в смысле методологии разработки. По большему счету все техники в программировании (функции, ООП и т.д.) - это о декомпозиции задачи. У вас есть большая задача, вы бьете ее на мелкие подзадачи. Чем подмодуль изолированее от других, тем понятнее. Я в своем проекте использую аналогичную библиотеку http://square.github.io/otto/ + инжекцию зависимостей http://square.github.io/dagger/ Получается приятно. Но опять повторюсь все это личный выбор программиста/комманды. Позволяет писать более понятно? - Используем. Т.е. не то, чтобы "вот есть event bus и все строем берем его и везде используем".

licensed under cc by-sa 3.0 with attribution.