EventBus parsing

Basic use of EventBus

1- Defines the event entity to be passed

public class MsgEvent { }
Copy the code

2- Register and unregister your subscribers

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    super.onStop();
    EventBus.getDefault().unregister(this);
}
Copy the code

3- Subscriber: Declare and annotate the subscription method

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MsgEvent event) {
    Log.d("OK");
}
Copy the code

4- Send events

EventBus.getDefault().post(new MsgEvent());
Copy the code

Difference between EventBus 3.x and 2.x usage

1- Defines the event entity to be passed

public class MsgEvent { }
Copy the code

2- Register and unregister your subscribers

Eventbus.getdefault ().register(this); Eventbus.getdefault ().register(this); EventBus.getDefault().register(this, 100); EventBus.getDefault().registerSticky(this, 100); EventBus.getDefault().registerSticky(this); Eventbus.getdefault ().unregister(this); eventbus.getDefault ().Copy the code

3- Subscriber: Declare and annotate the subscription method

Subscribe(threadMode = threadmode. BACKGROUND, sticky = true, Priority = 100) public void test(String STR) {} //2.x version public void onEvent(String STR) {} public void onEventMainThread(String str) { } public void onEventBackgroundThread(String str) { }Copy the code

4- Send events

Eventbus.getdefault ().post(" STR "); EventBus.getDefault().postSticky("str");Copy the code

Use of EventBus 3.x new features

EventBusAnnotationProcessor

EventBus2 uses Java reflection to iterate over fetch methods at runtime, and reflection has a performance cost. Use compile-time annotation since EventBus3, compile time using EventBusAnnotationProcessor annotation processor @ the Subscribe contains information, generate index class to save the subscriber and Subscribe to the relevance of information, Make it aware of the methods associated with subscription events before the eventbus.register () method is called.

The use of EventBusAnnotationProcessor

To use this new feature in EventBus 3, the following steps are required:

Add dependencies:

The compile 'org. Greenrobot: eventbus: 3.0.0'Copy the code

Since annotations depend on android-apt-plugin, we need to introduce apt in the dependencies of the project gradle

The classpath 'com. Neenbedankt. Gradle. Plugins: android - apt: 1.8'Copy the code

Build. Gradle for app Module with apt plugin and set the package name and class name of index generated by APT. If you do not set the package name and class name of index generated by APT, an error will be reported during compilation

apply plugin: 'com.neenbedankt.android-apt'

apt {
    arguments {
        eventBusIndex "com.demo.eventbusannotationsample.MyEventBusIndex"
    }
}
Copy the code

Finally, you need to introduce EventBusAnnotationProcessor in app module dependencies

Apt 'org. Greenrobot: eventbus -- the annotation processor: 3.0.1'Copy the code

After completing the above steps, recompile, can be in the app/build/generated/source/apt/debug/see the generated under MyEventBusIndex class

After recompiling, before using EventBus for the first time (such as in Application or SplashActivity), add the following code for Index to take effect:

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

It is important to note that if you don’t use EventBusAnnotationProcessor, 3 the EventBus parsing speed will be slower than the previous version.

EventBus principle

Defining the threading model

Public enum ThreadMode {/** * ThreadMode */ BACKGROUND, /** * ThreadMode */ MAIN}Copy the code

Annotations that define subscription event methods

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.MAIN;
}
Copy the code

Defining subscription methods

Public class SubscriberMethod {// private Method Method; Class private class <? > eventType; Private ThreadMode ThreadMode; public SubscriberMethod(Method method, Class<? > eventType, ThreadMode threadMode) { this.method = method; this.eventType = eventType; this.threadMode = threadMode; } public Method getMethod() { return method; } public Class<? > getEventType() { return eventType; } public ThreadMode getThreadMode() { return threadMode; }}Copy the code

Custom event bus

public class MyEventBus { private static volatile MyEventBus instance; // Object corresponds to an Activity or any class, List<SubscriberMethod> Methods that use Subscriber annotations in the corresponding class Private Map<Object, List<SubscriberMethod>> cacheMap; Private Handler mHandler; private MyEventBus() { cacheMap = new HashMap<>(); mHandler = new Handler(Looper.getMainLooper()); } // singleton public static MyEventBus getDefault() {if (instance == null) {synchronized (myeventbus.class) {if (instance == null) { instance = new MyEventBus(); } } } return instance; Public void register(Object obj) {public void register(Object obj) {public void register(Object obj) { Save to Map List<SubscriberMethod> List = cachemap.get (obj); if (list == null) { list = getSubscriberMethodReflection(obj); if (list.size() > 0) { cacheMap.put(obj, list); }} /** * Reflect the Subscriber annotation method in the fetch class ** @param obj class containing subscription events * @return class subscription events collection */ private List<SubscriberMethod> getSubscriberMethodReflection(Object obj) { List<SubscriberMethod> list = new ArrayList<>(); Class<? > clazz = obj.getClass(); while (clazz ! = null) { String name = clazz.getName(); if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.") || Name. StartsWith ("androidx.")) {// The system does not have a Subscribe annotation method, just jump out of the loop break; } Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Subscribe subscribe = method.getAnnotation(Subscribe.class); If (subscribe == null) {// Not subscribe annotation method continue; } Class<? >[] types = method.getParameterTypes(); if (types.length ! = 1) {throw new RuntimeException(" method parameters are not unique "); } // add Subscribe to the List ThreadMode ThreadMode = subscribe.threadmode (); SubscriberMethod subscriberMethod = new SubscriberMethod(method, types[0], threadMode); list.add(subscriberMethod); } // loop over the Subscribe annotation method in the parent class clazz = clazz.getsuperclass (); } return list; } /** * The logic in the POST method is to call the method in a class that uses the Subscriber annotation; Public void post(final Object msgEvent) {Set<Object> Set = cachemap.keySet (); Iterator<Object> iterator = set.iterator(); While (iterator.hasnext ()) {// Final Object obj = iterator.next(); List<SubscriberMethod> list = cacheMap.get(obj); for (final SubscriberMethod subscriberMethod : List) {/ / to determine whether a message type and subscribe method of the same type if (subscriberMethod. GetEventType () isAssignableFrom (msgEvent. GetClass ())) {/ / thread; Determine the Subscriber annotation method in which a thread executing the switch (subscriberMethod. GetThreadMode ()) {/ / Subscriber annotation method in the MAIN thread execution case MAIN: // Post is executed on the main thread, Call the subscriberMethod method if (looper.myLooper () == Looper.getMainLooper()) {invoke(subscriberMethod, obj, msgEvent); } else {// Execute post on child thread, Mhandler. post(new Runnable() {@override public void run() { invoke(subscriberMethod, obj, msgEvent); }}); } break; // Subscriber annotation method executes case BACKGROUND on child thread: If (looper.myLooper () == looper.getMainLooper ()) {// TODO the original EventBus // TODO uses PendingPostQueue to process messages} else {// Post is also executed on child threads, Call subscriberMethod invoke directly (subscriberMethod, obj, msgEvent); } break; default: break; }}}}} /** * Reflect the Subscriber annotation method in the calling class ** @param subscriberMethod Subscriber annotation method * @param obj class to subscribe to events * @param MsgEvent Subscribe event instance */ private void invoke(SubscriberMethod Subscribe Method, Object obj, Object msgEvent) { Method method = subscriberMethod.getMethod(); method.setAccessible(true); try { method.invoke(obj, msgEvent); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }}}Copy the code

APT Optimization principle of EventBus

MyEventBusIndex

/** * Analog APT optimization * No longer uses reflection to get the Subscriber annotation method in class * but code to get the Subscriber annotation method in class */ public class MyEventBusIndex {private static final Map<Object, List<SubscriberMethod>> cacheMap = new HashMap<>(); static { cacheMap.put(MainActivity.class.getSimpleName(), getSubscriberMethodMain()); cacheMap.put(Main2Activity.class.getSimpleName(), getSubscriberMethodMain2()); } public static List<SubscriberMethod> getSubscriberMethods(Object obj) {return cacheMap.get(obj.getClass().getSimpleName()); Private static List<SubscriberMethod> getSubscriberMethodMain() { List<SubscriberMethod> methodList = new ArrayList<>(); Try {// Add known class and method names to the SubscriberMethod The methodList add multiple SubscriberMethod methodList. Add (new SubscriberMethod (MainActivity. Class. GetMethod (" getMsgEvent ", MsgEvent.class), MsgEvent.class, ThreadMode.MAIN)); } catch (NoSuchMethodException | SecurityException e) { e.printStackTrace(); } return methodList; Private static List<SubscriberMethod> getSubscriberMethodMain2() { List<SubscriberMethod> methodList = new ArrayList<>(); try { methodList.add(new SubscriberMethod( Main2Activity.class.getMethod("getMsgEvent", MsgEvent.class), MsgEvent.class, ThreadMode.MAIN)); } catch (NoSuchMethodException | SecurityException e) { e.printStackTrace(); } return methodList; }}Copy the code

Reflection is no longer used when registering MyEventBus

Public void register(Object obj) {public void register(Object obj) {public void register(Object obj) { Save to Map List<SubscriberMethod> List = cachemap.get (obj); If (list = = null) {/ / through MyEventBusIndex direct access to the Subscriber list = MyEventBusIndex annotation method. The getSubscriberMethods (obj); if (list.size() > 0) { cacheMap.put(obj, list); }}}Copy the code

References:

Explain how to use EventBus 3.0

EventBus 3.0 source code analysis

Efficient use and source code parsing of EventBus 3.0

Parsing EventBusAnnotationProcessor

Deep understanding of EventBus source code

Hand rolled EventBus

EventBus goes from starter to setup