Comments 15
Использовал подобный подход на android — он там имеет дополнительные плюсы, так с UI можно работать только из основного потока, и реализован функциона передачи ивентов между UI-потоком и тред-пулом. Используется Roboguice для dependency injection.
Реализация:
GlobalEventManager
https://gist.github.com/naphaso/c9dbb8a4d4035d1f0584
умеет регистрировать/разрегистрировать обработчики сообщений с определённым типом TP — обрабатывать в тредпуле, UI — обабатывать в UI-потоке, IN — обрабатывать синхронно в потоке, пославшем сообщения (значительно быстрее, если обработчик сообщения отрабатывает очень быстро и не взаимодействует с UI).
Отнаследованы и подкручены тред-пулы, чтобы назначали красивые имена тредам, которые обрабатывают сообщения, чтобы было видно в логах, к обработке какого сообщения отновится каждая запись там.
EventTask
https://gist.github.com/naphaso/69de1a51ad383035dd04
Собственно, он и назначает красивые имена тредам.
EventListener
https://gist.github.com/naphaso/cc6d7b5da4de4c301f8d
Хранит WeakReference не объект и метод для обработки сообщения. WeakReference — чтобы, в случае, если объект забыл или физически не может (не знает, когда) отписться от приёма сообщений, это не приводило к трагическим последствиям в виде удерживания этого объекта в памяти.
Используется примерно так:
В любом объекте при его старте подписываемся на сообщения:
Когда уже не нужно принимать сообщения, отписываемся:
Как уже сказал, отписываться не обязательно, но желательно.
Обрабатываем сообщения в методах, аннотиованных следующим образом:
Ну, и полный пример:
За ошибки не ручаюсь, писал код прямо в комментарии. Потом, правда, этот подход пришлось немного переработать, но было весьма удобно.
Реализация:
GlobalEventManager
https://gist.github.com/naphaso/c9dbb8a4d4035d1f0584
умеет регистрировать/разрегистрировать обработчики сообщений с определённым типом TP — обрабатывать в тредпуле, UI — обабатывать в UI-потоке, IN — обрабатывать синхронно в потоке, пославшем сообщения (значительно быстрее, если обработчик сообщения отрабатывает очень быстро и не взаимодействует с UI).
Отнаследованы и подкручены тред-пулы, чтобы назначали красивые имена тредам, которые обрабатывают сообщения, чтобы было видно в логах, к обработке какого сообщения отновится каждая запись там.
EventTask
https://gist.github.com/naphaso/69de1a51ad383035dd04
Собственно, он и назначает красивые имена тредам.
EventListener
https://gist.github.com/naphaso/cc6d7b5da4de4c301f8d
Хранит WeakReference не объект и метод для обработки сообщения. WeakReference — чтобы, в случае, если объект забыл или физически не может (не знает, когда) отписться от приёма сообщений, это не приводило к трагическим последствиям в виде удерживания этого объекта в памяти.
Используется примерно так:
В любом объекте при его старте подписываемся на сообщения:
globalEventListener.registerListeners(this);
Когда уже не нужно принимать сообщения, отписываемся:
globalEventManager.unregisterListeners(this);
Как уже сказал, отписываться не обязательно, но желательно.
Обрабатываем сообщения в методах, аннотиованных следующим образом:
@Event(ThreadType.UI)
public void onSomeEvent(SomeEvent event) {
// some UI work
}
Ну, и полный пример:
@Singleton
public class SomeBackground {
@Inject
private GlobalEventManager globalEventManager;
@Inject
public void init() {
globalEventManager.registerListeners(this);
}
@Event(ThreadType.TP)
public void onStartBackgroundWork(BackgroundTaskEvent event) {
// some work
globalEventManager.fire(new PublishResultsEvent(results));
}
}
public class SomeActivity extends RoboActivity {
@Inject
private GlobalEventManager globalEventManager;
@Override
public void onCreate(Context context) {
super.onCreate(context);
globalEventManager.registerListeners(this);
}
@Override
public void onDestroy() {
super.onDestroy();
globalEventManager.unregisterListeners(this);
}
public void onButtonClickListener() {
globalEventManager.fire(new BackgroundTaskEvent(...));
}
@Event(ThreadType.UI)
public void onShowResults(PublishResultsEvent event) {
// show results in GUI
}
}
За ошибки не ручаюсь, писал код прямо в комментарии. Потом, правда, этот подход пришлось немного переработать, но было весьма удобно.
+3
Поддерживаю! Событийно-ориентированная парадигма в андроиде сглаживает многие шероховатости (например, коммуникацию фрагментов с активити или с сервисом). Лично я решил велосипедов не изобретать, а использовать простенький и крохотный EventBus — github.com/greenrobot/EventBus
0
Мне кажется, что это именно тот случай, когда коментарий информативней поста :)
0
два вопроса:
1. а при при использовании WeakReference как гарантируется сохранность слушателей?
2. кем обрабатываются аннотации?
1. а при при использовании WeakReference как гарантируется сохранность слушателей?
2. кем обрабатываются аннотации?
0
Чем дольше я программирую, тем больше мне нравятся слабосвязанные системы, которые состоят из большого числа разрозненных единиц (модулей), ничего не знающих друг о друге, но предполагающих существование других.
Двумя руками за. Сам стараюсь проектировать системы подобным образом.
Давно достали многомегабайтные монстры, загружающиеся минуту и больше, забивающие ОЗУ кодом, который не будет использован в текущей сессии, а может, и вообще никогда.
+1
При вызове у вас перебираются обработчики и вызываются. Если один из вызываемых обработчиков подпишется на то же событие еще раз или отпишется, то произойдет модификация коллекции, код упадёт и остальные обработчики не вызовутся. В этом месте нужно либо копировать список обработчиков в локальный, либо использовать одну из CopyOnWrite-коллекций для хранения обработчиков. А ссылка на гитхаб будет?
0
Спасибо, исправлю.
А репозиторий вот: github.com/lure0xaos/Events.git
А репозиторий вот: github.com/lure0xaos/Events.git
0
Как уже ответили выше, Вы изобрели велосипед, под названием EventBus. Есть много имплементаций, например, из Guava. Событийное программирование — это огромный антипаттерн, который использовать нужно с особой осторожностью, а по возможности избегать вообще.
0
Спасибо за подсказку, обязательно посмотрю. Но велосипеды — не так уж плохо, особенно если поставленную задачу выполняют неплохо — особенно столь несложные.
> Событийное программирование — это огромный антипаттерн
Простите, а почему так категорично? По-моему, я подробно описал как недостатки, так и достоинства событий (впрочем, это одно и то же). Но именно такое поведение мне и нужно, а следовательно — я предупрежден.
Например, это напоминает подавление NPE исключения при вызове метода на несуществующем объекте — но так же просто логгировать или добавить его во всех местах.
Я не говорю, что надо вообще все им заменить. Но в определенных случаев — более чем красиво и удобно.
> Событийное программирование — это огромный антипаттерн
Простите, а почему так категорично? По-моему, я подробно описал как недостатки, так и достоинства событий (впрочем, это одно и то же). Но именно такое поведение мне и нужно, а следовательно — я предупрежден.
Например, это напоминает подавление NPE исключения при вызове метода на несуществующем объекте — но так же просто логгировать или добавить его во всех местах.
Я не говорю, что надо вообще все им заменить. Но в определенных случаев — более чем красиво и удобно.
0
Событийное программирование — это огромный антипаттерн
Эм. А можно поподробнее?
0
— разобщает логику и делает запутанным код системы
— сильно затрудняет нахождение ошибок и отладку
— не гарантирует и не контролирует последовательность обработки событий и соответственно делает непредсказуемым поведение системы
Я раньше много писал на свинге и знаю что такое. И в самой библиотеке были баги и практически все приложения подглючивали.
Поэтому если можно обойтись без событийной модели — оно всегда предпочтительней.
— сильно затрудняет нахождение ошибок и отладку
— не гарантирует и не контролирует последовательность обработки событий и соответственно делает непредсказуемым поведение системы
Я раньше много писал на свинге и знаю что такое. И в самой библиотеке были баги и практически все приложения подглючивали.
Поэтому если можно обойтись без событийной модели — оно всегда предпочтительней.
0
> разобщает логику и делает запутанным код системы
Наоборот, заставляет систему разбивать на независимые легкотестируемые модули
> сильно затрудняет нахождение ошибок и отладку
Наоборот, расширяет свободу тестам. всегда можно выделить границы системы и промоделировать события, зачастую — в автоматическом режиме.
> не гарантирует и не контролирует последовательность обработки событий и соответственно делает непредсказуемым поведение системы
тогда, по логике вещей, должен быть один главный цикл, который и опрашивает устройства ввода, и осуществляет вывод. Но нет, системы существуют и тестируются. Просто подход другой, и да, на порядок сложнее (надо же угадать, что баг проявляется не всегда, а -от зависимости фазы луны- т.е. от последовательности событий с наложением в мнтервале 120мс)
Посему, с одной стороны, так легче, с другой стороны — к черту старый подход отладки.
Наоборот, заставляет систему разбивать на независимые легкотестируемые модули
> сильно затрудняет нахождение ошибок и отладку
Наоборот, расширяет свободу тестам. всегда можно выделить границы системы и промоделировать события, зачастую — в автоматическом режиме.
> не гарантирует и не контролирует последовательность обработки событий и соответственно делает непредсказуемым поведение системы
тогда, по логике вещей, должен быть один главный цикл, который и опрашивает устройства ввода, и осуществляет вывод. Но нет, системы существуют и тестируются. Просто подход другой, и да, на порядок сложнее (надо же угадать, что баг проявляется не всегда, а -от зависимости фазы луны- т.е. от последовательности событий с наложением в мнтервале 120мс)
Посему, с одной стороны, так легче, с другой стороны — к черту старый подход отладки.
+1
Идеальная система имеет древовидную структуру компонентов. Если есть возможность сделать ее такой, такой ее и нужно делать. Событийная модель может быть полезна:
— когда компоненты достаточно независимы и слабо связаны функционально
— при асинхронном взаимодейтвии компонентов
— когда нужно связать две несовместимые модели, например модель данных приложения и GUI
— в качестве локальных callback-ов от компонентов низкого уровня к более высоким, если нельзя обойтись без них
Потенциальные проблемы, за которыми нужно будет следить, если испльзовать события:
— регистрация/дерегистрация хендлеров. Как правило 80% всех ошибок в свинге, связанных с memory-leaking. Спасает EventBus с weak хендлерами.
— когда в процессе обработки события одини из хендлеров генерирует новое. Логика подсказывает, что оно должно обработаться после текущего, однако реально оно обрабатывается синхронно прямо в момент выкидывания. Частично спасает EventBus с асинхронной очередью, ибо в очереди уже могут стоять другие события, которые к моменту обработки данного делают состояние системы несовместимым. Например, кнопка окна выкинула, что она нажалась, а следующим в очереди уже стояло закрытие окна. В итоге обработчик получил нажатие кнопки, которая уже не существует, что могло бы спровоцировать трудноотлавливаемую ошибку.
— запутанные зависимости и дилема: в какой последовательности правильно останавливать компоненты?
— ну и классика при использовании в multithreaded environment: дедлоки всех возможных видов.
— когда компоненты достаточно независимы и слабо связаны функционально
— при асинхронном взаимодейтвии компонентов
— когда нужно связать две несовместимые модели, например модель данных приложения и GUI
— в качестве локальных callback-ов от компонентов низкого уровня к более высоким, если нельзя обойтись без них
Потенциальные проблемы, за которыми нужно будет следить, если испльзовать события:
— регистрация/дерегистрация хендлеров. Как правило 80% всех ошибок в свинге, связанных с memory-leaking. Спасает EventBus с weak хендлерами.
— когда в процессе обработки события одини из хендлеров генерирует новое. Логика подсказывает, что оно должно обработаться после текущего, однако реально оно обрабатывается синхронно прямо в момент выкидывания. Частично спасает EventBus с асинхронной очередью, ибо в очереди уже могут стоять другие события, которые к моменту обработки данного делают состояние системы несовместимым. Например, кнопка окна выкинула, что она нажалась, а следующим в очереди уже стояло закрытие окна. В итоге обработчик получил нажатие кнопки, которая уже не существует, что могло бы спровоцировать трудноотлавливаемую ошибку.
— запутанные зависимости и дилема: в какой последовательности правильно останавливать компоненты?
— ну и классика при использовании в multithreaded environment: дедлоки всех возможных видов.
0
Sign up to leave a comment.
Каркас для Event-Driven программирования