A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/google/guava/wiki/EventBusExplained below:

EventBusExplained · google/guava Wiki · GitHub

EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration. It is not a general-purpose publish-subscribe system, nor is it intended for interprocess communication.

We recommend against using EventBus. It was designed many years ago, and newer libraries offer better ways to decouple components and react to events.

To decouple components, we recommend a dependency-injection framework. For Android code, most apps use Dagger. For server code, common options include Guice and Spring. Frameworks typically offer a way to register multiple listeners independently and then request them together as a set (Dagger, Guice, Spring).

To react to events, we recommend a reactive-streams framework like RxJava (supplemented with its RxAndroid extension if you are building for Android) or Project Reactor. (For the basics of translating code from using an event bus to using a reactive-streams framework, see these two guides: 1, 2.) Some usages of EventBus may be better written using Kotlin coroutines, including Flow and Channels. Yet other usages are better served by individual libraries that provide specialized support for particular use cases.

Disadvantages of EventBus include:

// Class is typically registered by the container.
class EventBusChangeRecorder {
  @Subscribe public void recordCustomerChange(ChangeEvent e) {
    recordChange(e.getChange());
  }
}
// somewhere during initialization
eventBus.register(new EventBusChangeRecorder());
// much later
public void changeCustomer()
  ChangeEvent event = getChangeEvent();
  eventBus.post(event);
}

Converting an existing EventListener-based system to use the EventBus is easy.

To listen for a specific flavor of event (say, a CustomerChangeEvent)...

To register your listener methods with the event producers...

To listen for a common event supertype (such as EventObject or Object)...

To listen for and detect events that were dispatched without listeners...

To keep track of listeners to your events...

To dispatch an event to listeners...

The EventBus system and code use the following terms to discuss event distribution:

Event Any object that may be posted to a bus. Subscribing The act of registering a listener with an EventBus, so that its subscriber methods will receive events. Listener An object that wishes to receive events, by exposing subscriber methods. Subscriber method A public method that the EventBus should use to deliver posted events. Subscriber methods are marked by the @Subscribe annotation. Posting an event Making the event available to any listeners through the EventBus. Why must I create my own Event Bus, rather than using a singleton?

EventBus doesn't specify how you use it; there's nothing stopping your application from having separate EventBus instances for each component, or using separate instances to separate events by context or topic. This also makes it trivial to set up and tear down EventBus objects in your tests.

Of course, if you'd like to have a process-wide EventBus singleton, there's nothing stopping you from doing it that way. Simply have your container (such as Guice) create the EventBus as a singleton at global scope (or stash it in a static field, if you're into that sort of thing).

In short, EventBus is not a singleton because we'd rather not make that decision for you. Use it how you like.

Can I unregister a listener from the Event Bus?

Yes, using EventBus.unregister, but we find this is needed only rarely:

Why use an annotation to mark subscriber methods, rather than requiring the listener to implement an interface?

We feel that the Event Bus's @Subscribe annotation conveys your intentions just as explicitly as implementing an interface (or perhaps more so), while leaving you free to place event subscriber methods wherever you wish and give them intention-revealing names.

Traditional Java Events use a listener interface which typically sports only a handful of methods -- typically one. This has a number of disadvantages:

The difficulties in implementing this cleanly has given rise to a pattern, particularly common in Swing apps, of using tiny anonymous classes to implement event listener interfaces.

Compare these two cases:

class ChangeRecorder {
  void setCustomer(Customer cust) {
    cust.addChangeListener(new ChangeListener() {
      public void customerChanged(ChangeEvent e) {
        recordChange(e.getChange());
      }
    };
  }
}

versus

// Class is typically registered by the container.
class EventBusChangeRecorder {
  @Subscribe public void recordCustomerChange(ChangeEvent e) {
    recordChange(e.getChange());
  }
}

The intent is actually clearer in the second case: there's less noise code, and the event subscriber has a clear and meaningful name.

What about a generic Subscriber<T> interface?

Some have proposed a generic Subscriber<T> interface for EventBus listeners. This runs into issues with Java's use of type erasure, not to mention problems in usability.

Let's say the interface looked something like the following:

interface Subscriber<T> {
  void handleEvent(T event);
}

Due to erasure, no single class can implement a generic interface more than once with different type parameters. This is a giant step backwards from traditional Java Events, where even if actionPerformed and keyPressed aren't very meaningful names, at least you can implement both methods!

Doesn't EventBus destroy static typing and eliminate automated refactoring support?

Some have freaked out about EventBus's register(Object) and post(Object) methods' use of the Object type.

Object is used here for a good reason: the Event Bus library places no restrictions on the types of either your event listeners (as in register(Object)) or the events themselves (in post(Object)).

Event subscriber methods, on the other hand, must explicitly declare their argument type -- the type of event desired (or one of its supertypes). Thus, searching for references to an event class will instantly find all subscriber methods for that event, and renaming the type will affect all subscriber methods within view of your IDE (and any code that creates the event).

It's true that you can rename your @Subscribed event subscriber methods at will; Event Bus will not stop this or do anything to propagate the rename because, to Event Bus, the names of your subscriber methods are irrelevant. Test code that calls the methods directly, of course, will be affected by your renaming -- but that's what your refactoring tools are for. We see this as a feature, not a bug: being able to rename your subscriber methods at will lets you make their meaning clearer.

What happens if I register a listener without any subscriber methods?

Nothing at all.

The Event Bus was designed to integrate with containers and module systems, with Guice as the prototypical example. In these cases, it's convenient to have the container/factory/environment pass every created object to an EventBus's register(Object) method.

This way, any object created by the container/factory/environment can hook into the system's event model simply by exposing subscriber methods.

What Event Bus problems can be detected at compile time?

Any problem that can be unambiguously detected by Java's type system. For example, defining a subscriber method for a nonexistent event type.

What Event Bus problems can be detected immediately at registration?

Immediately upon invoking register(Object) , the listener being registered is checked for the well-formedness of its subscriber methods. Specifically, any methods marked with @Subscribe must take only a single argument.

Any violations of this rule will cause an IllegalArgumentException to be thrown.

(This check could be moved to compile-time using APT, a solution we're researching.)

What EventBus problems may only be detected later, at runtime?

If a component posts events with no registered listeners, it may indicate an error (typically an indication that you missed a @Subscribe annotation, or that the listening component is not loaded).

(Note that this is not necessarily indicative of a problem. There are many cases where an application will deliberately ignore a posted event, particularly if the event is coming from code you don't control.)

To handle such events, register a subscriber method for the DeadEvent class. Whenever EventBus receives an event with no registered subscribers, it will turn it into a DeadEvent and pass it your way -- allowing you to log it or otherwise recover.

How do I test event listeners and their subscriber methods?

Because subscriber methods on your listener classes are normal methods, you can simply call them from your test code to simulate the EventBus.

Why can't I do <magic thing> with EventBus?

EventBus is designed to deal with a large class of use cases really, really well. We prefer hitting the nail on the head for most use cases to doing decently on all use cases.

Additionally, making EventBus extensible -- and making it useful and productive to extend, while still allowing ourselves to make additions to the core EventBus API that don't conflict with any of your extensions -- is an extremely difficult problem.

If you really, really need magic thing X, that EventBus can't currently provide, you should design your own alternative.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.3