Szyna zdarzeń
Szyna zdarzeń EventBus upraszcza komunikacje pomiędzy różnymi komponentami systemu Android (Activity, Fragment, Service itp) za pomocą zdarzeń (event) co szczególnie może być przydatne w zadaniach asynchronicznych. Jest realizacją wzorca Obserwator (Observer) - wprowadza podział na nadawców oraz odbiorców. Zdarzenie emitowane jest przez jednego nadawcę i trafia do wszystkich aktywnych odbiorców zarejestrowanych do szyny zdarzeń. Dobrze radzi sobie z cyklem życia komponentów oraz wątkami tła, posiada lekką strukturę, unika złożonych zależności i upraszcza kod. Jednakże z momentem wprowadzenia, stabilizacji i zyskiwania popularności programowania reaktywnego w RxJava, architektura szyny zdarzeń staje się przestarzała. Oba projekty implementują ten sam model programowania oparty o zdarzenia lecz RxJava jest bardziej wydajne i zapewnia lepszą kontrolę nad wątkami przez co wydaje się naturalnym następnikiem. Konfiguracja i zmiana domyślnego zachowania instancji EventBus powinna zostać przeprowadzona przed pierwszym użyciem instancji szyny zdarzeń.
class App : Application() {
override fun onCreate() {
super.onCreate()
//customize default EventBus instance must be called before first subscriber called
EventBus.builder()
.logNoSubscriberMessages(false) //do not log when no subscriber exists
.sendNoSubscriberEvent(false) //do not send SubscriberEvent if no subscriber exists
.throwSubscriberException(BuildConfig.DEBUG) //throw exceptions only in debug mode
.installDefaultEventBus()
//or create custom instance by using build method
}
}Rejestracja
Aby móc odbierać zdarzenia zarówno subskrybenci powinni dokonać rejestracji do instancji szyny zdarzeń EventBus zgodnie ze swoim cyklem życia. Należy również pamiętać o prawidłowym odrejestrowaniu komponentów co w przeciwnym razie może prowadzić do wycieku pamięci.
//some Android component class
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
//register in proper place of lifecycle
public override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
//unregister to avoid leak memory
public override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
}Zdarzenie
Zdarzenie jest niczym innym jak emisją obiektu danej klasy przez nadawcę. W sytuacji, gdy pożądane jest rozróżnienie zdarzeń o tej samej strukturze należy stworzyć dla każdego rodzaju zdarzenia klasę opakowującą (wrapper). Dobrą praktyką jest zawieranie w nazwie klasy słowa Event.
data class MessageEvent(val number: Int, val text: String) {
//more fields
}
//if want emit some primitive type create class wrapper
//Custom1Event and Custom2Event have the same properties but are different events
class Custom1Event(val message: String) {
}
class Custom2Event(val name: String) {
}Nadawca
Wysyłania zdarzenia zachodzi na instancji EventBus z dowolnego fragmentu kodu i przeprowadzane jest przez przekazywanie obiektu do metody post lub postSticky (dla zdarzenia, które ma zostać zapamiętane do przyszłego użycia).
EventBus.getDefault().post(MessageEvent(100, "text"))
EventBus.getDefault().postSticky(Custom2Event("Johnnie"))Subskrybent
Aby zarejestrowany komponent mógł reagować na emisję danego zdarzenia powinien implementować metodę oznaczoną adnotacją @Subscribe i przyjmującą jako parametr wybrany typ (nazwa metody nie ma znaczenia). Dodatkowo istnieje możliwość ustawienia wątku roboczego na jakim odebrane zdarzenie będzie wykonywało pracę (threadMode), ustalenie priorytetu subskrypcji w obrębie tego samego wątku (priority) czy zdecydowanie o pobieraniu ostatniej znanej instancji zdarzenia (sticky). Każdy subskrybent zostanie wywołane dokładnie raz.
//choose one of POSTING, MAIN, BACKGROUND, MAIN_ORDERED, ASYNC
@Subscribe(threadMode = ThreadMode.MAIN) //ThreadMode.POSTING is by default
fun onMessageEvent(event: MessageEvent) {
//do something with received event on the main UI thread
}
//priority 0 is default
@Subscribe
fun onMessageEvent(event: Custom1Event) {
//do something with received event on the background thread
//on some conditions
//prevent delivery to other subscribers with lower priority
EventBus.getDefault().cancelEventDelivery(event)
}
@Subscribe(priority = 1)
fun onMessageEventExtra(event: Custom1Event) {
//do something with received event on the background thread
}
//received value only from events posted by postSticky
@Subscribe(sticky = true)
fun onMessageEvent(event: Custom2Event) {
//receive previously posted sticky event immediately
//listen for new event
}