EventBus

Biblioteki

  |   4 min czytania
(EventBus 3.1)

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
}