Related articles:

  • What does EventBus 3.0 feature and how to use it
  • How does EventBus 3.0 implement the EventBus

The goal is to go beyond EventBus 3.0, which we are already familiar with from our previous experience, so let’s summarize what we can learn from this framework.

After reading this article, you will know:

  • What problem does EventBus solve
  • The thought of the EventBus
  • Compile-time annotations for EventBus
  • Design patterns used by EventBus
  • Details worth learning
  • Deficiency in
  • feeling

Let’s start by looking at what EventBus solves.

What problem does EventBus solve

In daily development, callback is used in many scenarios, such as button click events, network request results and so on. It represents the monitoring of a possible event in the future. The specific use steps are as follows:

  1. Create a callback interface that defines the actions to be performed when an event occurs
  2. Create a concrete implementation of the callback where you need to listen and pass it on to the event trigger
  3. The event trigger holds a reference to the callback interface and invokes the concrete implementation of the callback interface when an event occurs

Listening for future events is implemented in three very simple steps.

If you have multiple listeners for an event, you need to create a list of listeners in the event trigger and notify registered listeners one by one when the event occurs. This is the observer model.

The observer mode, also known as the “publisk-subscribe mode”, is used for an observer to hold references to multiple observer objects. When the status of the observed changes, all observers are notified to update. It’s a one-to-many dependency.

Welcome to my observer mode if you are not familiar with it: a cloud piercing arrow, thousands of armies and horses to meet and understand the observer mode.

In the observer mode, subscribers need to implement the same interface, that is, only listen for the same events. If you want to listen for different events, you need to create different interfaces, which will inevitably be a bit cumbersome after more events.

It would be nice to have a way for subscribers to implement an interface that can listen for different events, no, not implement the interface at all, and just create the actions to be performed when the event occurs.

The idea that EventBus represents is one solution.

The thought of the EventBus

In EventBus, we don’t need to implement interfaces; we just create methods in subscribers that listen for different events and then annotate them.

EventBus gets the subscription method and its class at compile time and run time (depending on whether you index it or not) by handling annotations and reflections, and then stores the subscriber, the subscription method, and the subscribed event in two separate properties.

When a sender sends an event, EventBus finds the subscriber and subscription method in the previously saved properties based on the event and invokes it reflectively.

Compile-time annotations for EventBus

EventBus’s annotation processor reads annotations and generates index files at compile time.

Previously, EventBus was pure reflection, and people often complained about poor performance, so compile-time annotations can speed up a lot of lookups.

In addition to ButterKnife’s use of compile-time annotations to generate duplicate code, EventBus’s use of compile-time annotations gives us new ideas:

  • Move run-time lookups to compile time
  • Use a hash table to save the information found
  • The generated class implements the convention’s interface for easy run-time invocation

For details on how compile-time annotations can be used, see this article: Simple Butterknife-like effects with compile-time annotations

Design patterns used by EventBus

As a more mature framework, EventBus uses a number of design patterns, which are reviewed here.

① Singleton mode

static volatile EventBus defaultInstance;
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = newEventBus(); }}}return defaultInstance;
}Copy the code

Classic double-check singleton, using volatile modifier to avoid reordering, perfect.

(2) the Builder pattern

public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBus(EventBusBuilder builder) {
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this); indexCount = builder.subscriberInfoIndexes ! =null ? builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}Copy the code

It’s too tiring to call setXXX() every time a class has too many fields. Instead, provide a Builder with some default values and let the user set the rest.

③ Appearance mode

EventBus encapsulates the saving and distribution of different events internally and provides simple methods for registering, sending, and deregistration externally.

This is the appearance pattern, when repeated operations scattered in multiple places are brought into a single entry.

④ Strategy mode

private final HandlerPoster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;Copy the code

EventBus’s several message posters do not strictly conform to the strategy pattern, but the idea is the same. They all do the same thing, just in different ways.

It is perfectly possible to have a unified Poster interface called enqueue(), and then EventBus holds three references to Poster, each with a different implementation.

⑤ Observer model

Needless to say, the EventBus process of subscribing to and publishing events is an improved version of the Observer model.

For articles on design patterns, see the Design Patterns column

Details worth learning

If I were asked to write an EventBus now, I would not be able to do so.

Even if the overall idea (event registration, collection, sending, unregistration) has been divided into different modules, how to achieve the specific, think about headache oh.

Take a look at the details of EventBus to learn:

  • Provide default Builder and default instance
  • Choose the appropriate thread pool

    • Executors.newCachedThreadPool()Suitable for performing a large number of small, short-term tasks concurrently
  • useThreadLocalImplement thread independence of event queues
  • Concurrency control

    • Data can be added and used at the same timeCopyOnWriteArrayListsynchronized
  • Separation of duties

    • Subscription methods in the lookup class have special classesSubscriberMethodFinder
    • Save classes have special classes for their subscription method relationshipsSubscription
    • There are also special classes for events sent by different threads
  • Use a map to cache information that might be reused

    • Mapping of events to their parent classes and interfacesMap<Class<? >, List<Class<? >>> eventTypesCache = new HashMap<>()
    • Events are associated with a list of corresponding subscribersMap<Class<? >, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
    • A subscriber is associated with a list of subscribed eventsMap<Object, List<Class<? >>> typesBySubscriber
    • Preservation viscosity eventMap<Class<? >, Object> stickyEvents
  • If you need to create a large number of objects of the same type, consider using an object pool, which can be recycled after the object is used, as long as references to other objects are set to NULL

    • FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]
    • List<PendingPost> pendingPostPool = new ArrayList<PendingPost>()
  • Encapsulate several arguments passed repeatedly into an object

    • PostingThreadState
  • Created its own event queuePendingPostQueue

    • Two-way linked list
    • Producer-consumer model, waiting-in-queue nofityAll

Deficiency in

Eventbus.getdefault () = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder = EventBusBuilder

EventBus mInstance = EventBus.builder(a).addIndex(new MyEventBusIndex())
        .build(a);Copy the code

(2) Decoupling with EventBus is a double-edged sword, with a large amount of business logic scattered all over the place, similar to goto in C:

  1. When the program is relatively simple, it is more flexible, but when the program is more complex, it is easy to cause the chaos of the program flow
  2. Not write yourself, others see the program is not so easy to understand
  3. The process of debugging the program can also become difficult.

From: zhidao.baidu.com/question/14…

③Poster does not create a base class/interface

It’s not a big deal, but it would be nice to create a unified interface. 0.0

feeling

EventBus is still a great framework, mainly because it came out early and has a good idea.

Before writing this article, I summarized some of the design concepts in the EventBus source code, such as thread pools, blocking queues, and compile-time annotations.

Fundamentals are important. How can you write code like this without fundamentals? ! We’re gonna have to work a little harder.