preface

The basic use of EventBus, portal, was described in the previous post: In this article, we’ll explore the fun and advanced uses of EventBus step by step. There are mainly

  • Thread Mode
  • Configuration
  • Sticky Events
  • Priorities and Event Cancellation function
  • Subscriber Index
  • AsyncExecutor

Without further ado, let’s get started

Advanced usage

Thread Mode

Remember from the last article, the information contained in the @Subscribe method

@Subscribe(threadMode = ThreadMode.MAIN)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    mTextView.setText("There's only one Eason Chan");
Copy the code

Threadmode. MAIN is just one way to determine which thread the subscription method will execute on by specifying threadMode. Take a look at the others

public enum ThreadMode {
    /**
     * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for* simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers * using  this mode mustreturn quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,

    /**
     * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is * the main thread, event handler methods will be called directly. Event handlers using this mode must return * quickly to avoid blocking the main thread. */ MAIN, /** * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to * return quickly to avoid blocking the background thread. */ BACKGROUND, /** * Event handler methods are called in a separate thread. This is always independent from the posting thread and the * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number * of long running asynchronous handler methods at the same time  to limit the number of concurrent threads. EventBus * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications. */ ASYNC }Copy the code

At a glance, the notes summarize the application scenarios of the four modes

  • POSTING: Events are posted on the same thread as subscribed.
  • MAIN: Events are subscribed to in the MAIN thread regardless of which thread they are published in.
  • BACKGROUND: Subscribes to the thread if it is not the main thread, or to a separate BACKGROUND thread if it is the main thread.
  • ASYNC: Subscribes with thread pool threads.

Such as

@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    mTextView.setText("There's only one Eason Chan");
}
Copy the code

We then subscribe to events in the background, but since UI updates are only in the main thread, the following exception occurs

Could not dispatch event: class com.charmingwong.androidtest.UpdateUIEvent to subscribing class class com.charmingwong.androidtest.MainActivity
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Copy the code

Configuration

The default way to get an EventBus instance is

EventBus.getDefault().register(this);
Copy the code

In this way, the attributes of EventBus are obtained by default, which sometimes does not meet our requirements. In this case, we can use EventBusBuilder to configure EventBus attributes

EventBus eventBus = EventBus.builder().eventInheritance(true)
    .ignoreGeneratedIndex(false)
    .logNoSubscriberMessages(true)
    .logSubscriberExceptions(false)
    .sendNoSubscriberEvent(true)
    .sendSubscriberExceptionEvent(true)
    .throwSubscriberException(true)
    .strictMethodVerification(true)
    .build();
eventBus.register(this);
Copy the code

Builder () returns EventBusBuilder ()

// Create a default EventBus object, equivalent to eventbus.getDefault (). EventBus installDefaultEventBus() : // Add the index generated by the EventBus "annotation preprocessor EventBuilder addIndex(SubscriberInfoIndex index) : // By default, EventBus assumes that the event class has a hierarchy (the subscriber superclass will be notified) / / define a thread pool is used for processing the background thread and asynchronous thread distribute events EventBuilder executorService (Java. Util. Concurrent. The executorService executorService) : // Sets the subscription index to be ignored, even if the event is indexed. Default isfalseEventBuilder ignoreGeneratedIndex(Boolean ignoreGeneratedIndex) : // Prints no subscription message, default istrue
EventBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) : // Print subscription exception, defaulttrue
EventBuilder logSubscriberExceptions(boolean log// Sets whether EventBus will remain silent when events are sent without subscribers. DefaulttrueEventBuilder sendNoSubscriberEvent(Boolean sendNoSubscriberEvent) : // An exception sent to distribute events. DefaulttrueEventBuilder sendSubscriberExceptionEvent (Boolean sendSubscriberExceptionEvent) : Before 3.0, the method name that receives a processing event starts with onEvent. Avoid method name validation that does not start with onEvent. Enable strict method validation (default:false) EventBuilder strictMethodVerification (Java. Lang. Class <? > clazz) // If an exception occurs in the onEvent*** method, whether to distribute this exception to subscribers (default:false(Boolean) EventBuilder throwSubscriberException throwSubscriberException)Copy the code

In this way, we have the flexibility to use EventBus.

Sticky Events

After a sticky event is published, subsequent subscribers can no longer receive it. After a Sticky event is published, EventBus will save it until a new sticky event of the same type is published, and then the old one will be overwritten. Therefore, subsequent subscribers can only use sticky mode. You can still get the sticky event. The usage is as follows:

Subscribe to the sticky event in SecondActivity and register

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main); // Register the page as a subscriber eventbus.getDefault ().register(this); } @Subscribe(sticky =true)
public void onMyStickyEvent(MyStickyEvent myStickyEvent) {
    mTextView.setText("Someone asked you if he was Eason Chan.");
}

Copy the code

Publish events in MainActivity

@override public void onClick(View v) {eventBus.getDefault ().poststicky (new MyStickyEvent()); Intent intent = new Intent(MainActivity.this, SecondActivity.class); startActivity(intent); }Copy the code

Take a look at the results:

Before MainActivity starts, publish the sticky event, and then SecondActivity starts again. After registering as a subscriber, the user can still receive the sticky event. The success proves the function of sticky and also verifies the above statement. After the sticky event is published, it can also be removed

MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent ! = null) { // Nowdo something with it
}
Copy the code

Priorities and Event Cancellation

Here is an example to verify the priority relationship

// MainActivity
@Subscribe(priority = 2)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 2");
}

// SecondActivity
@Subscribe(priority = 1)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 1");
}
Copy the code

Both activities subscribe to the same event type, but with different priorities, and publish events in SecondActivity

Eventbus.getdefault ().post(new UpdateUIEvent());Copy the code

To see the running effect, log:

D/MainActivity: onUpdateUIEvent: priority = 2
D/SecondActivity: onUpdateUIEvent: priority = 1
Copy the code

Switch priorities again

// MainActivity
@Subscribe(priority = 1)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 1");
}

// SecondActivity
@Subscribe(priority = 2)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 2");
}
Copy the code

Run, see log:

D/SecondActivity: onUpdateUIEvent: priority = 2
D/MainActivity: onUpdateUIEvent: priority = 1
Copy the code

Priority determines the order in which events are received. The higher the priority is, the earlier the events are received. The subscriber that receives the event first can also intercept the event and cancel further delivery, and subsequent lower-priority subscribers will not receive the event again. We can set the cancel event to continue delivery like this:

@Subscribe(priority = 2)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 2");
    EventBus.getDefault().cancelEventDelivery(updateUIEvent);
}
Copy the code

Run, see log:

D/SecondActivity: onUpdateUIEvent: priority = 2
Copy the code

Sure enough, the subscriber with Priority =1 did not receive the event, which is EventBus’s ability to cancel the event and continue delivery.

Subscriber Index

After reading the official document, we know that Subscriber Index is a new technology on EventBus 3, so it is also suggested that those who have not learned EventBus can skip the version before 2.x and directly learn the latest version. About the characteristics of The Subscriber Index technology of EventBus, please translate the official explanation:

It is an optional optimization to speed up initial subscriber registration.

Subscriber Index is an optional optimization technique to speed up initial Subscriber registration.

The subscriber index can be created during build time using the EventBus annotation processor. While it is not required to use an index, it is recommended on Android for best performance.

The Subscriber Index is created at compile time using the EventBus annotation processor, and while it is not mandatory, it is officially recommended as it has the best performance on Android.

How to use: In Gradle do the following configuration

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
            }
        }
    }
}
 
dependencies {
    compile 'org. Greenrobot: eventbus: 3.1.1'
    annotationProcessor 'org. Greenrobot: eventbus -- the annotation processor: 3.1.1'
}
Copy the code

If the above method does not work, you can also use the Android-apt Gradle plugin

buildscript {
    dependencies {
        classpath 'com. Neenbedankt. Gradle. Plugins: android - apt: 1.8'
    }
}
 
apply plugin: 'com.neenbedankt.android-apt'
 
dependencies {
    compile 'org. Greenrobot: eventbus: 3.1.1'
    apt 'org. Greenrobot: eventbus -- the annotation processor: 3.1.1'
}
 
apt {
    arguments {
        eventBusIndex "com.example.myapp.MyEventBusIndex"}}Copy the code

Once configured, we can add indexes to our code

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
Copy the code

In addition to Application, you can also use indexes in the Library

EventBus eventBus = EventBus.builder()
    .addIndex(new MyEventBusAppIndex())
    .addIndex(new MyEventBusLibIndex()).build();
Copy the code

Ok, so that’s how indexes are used.

AsyncExecutor

Let’s look at an example

AsyncExecutor.create().execute(
    new AsyncExecutor.RunnableEx() { @Override public void run() throws Exception { // No need to catch any Exception (here: LoginException) prepare(); EventBus.getDefault().postSticky(new MyStickyEvent()); }}); private void prepare() throws Exception { throw new Exception("prepare failed");
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: ");
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void handleFailureEvent(ThrowableFailureEvent event) {
    Log.d(TAG, "handleFailureEvent: " + event.getThrowable().getMessage());
Copy the code

In this way, if an exception occurs in the relevant code, the exception is encapsulated as a ThrowableFailureEvent, which is automatically published. Once the subscriber defines a method to receive the ThrowableFailureEvent, the exception information is available. Subsequent Update euievent will not be published, and if no exception occurs, the normal publishing event will occur. Run, see log:

D/SecondActivity: handleFailureEvent: prepare failed
Copy the code

Sure enough, only handleFailureEvent(ThrowableFailureEvent) received an exception event. The advantage of this approach is that the exception information can be handed to the subscriber, who can deal with it accordingly.

conclusion

There are a few other features of EventBus that you can see in the official documentation. If you don’t understand something, it’s important to check the official documentation.

Poke me, read EventBus official documentation!

The next article focuses on the source code for EventBus and how it works.

Thanks for reading!