EventBus

news2024/9/25 13:25:04

EventBus

文章目录

  • EventBus
    • 1.EventBus的作用
    • 2.关于EventBus的概述
    • 3.EventBus的使用方法
    • 4.EventBus的黏性事件
    • 5.EventBus的源码
      • EventBus的构造方法
        • getDefault()源码
        • EventBus()源码
      • 订阅者注册
        • register()源码
        • findSubscriberMethods()源码
          • findUsingInfo()源码
          • findUsingReflectionInSingleClass()源码
          • findSubscriberMethods()流程图操作
        • subscribe()源码
      • 事件发送
        • postSingleEvent()源码
          • postSingleEventForEventType()源码
          • postToSubscription()源码
      • 订阅者取消注册

1.EventBus的作用

在没用EventBus的时候我们想想,我们是怎么样将第二个Activity的数据传递给第一个Activity的,还有怎么将第一个Activity的数据传递给第二个的,现在不回去看已经忘的差不多了。我们再想想ActivityActivity传数据还知道,那么FragmentFragment怎么传递数据。这就不知道了,为了解决这个问题,我们将使用组件化里面使用过的一个第三方库EventBus

EventBus简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。

2.关于EventBus的概述

1、三个角色
Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。
Subscriber:事件订阅者,在EventBus
3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe,并且指定线程模型,默认是POSTING。
Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
2.四种线程模型
EventBus3.0有四种线程模型,分别是:

POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。
MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。

3.EventBus的使用方法

首先先导入:

implementation("org.greenrobot:eventbus:3.3.1")

然后自定义一个事件类

public class EventBus {
    public EventBus(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    private String message;
}

第一个Activity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView tv_message;
    private Button bt_message;
    private Button bt_subscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_message = findViewById(R.id.tv_message);
        bt_message = findViewById(R.id.bt_message);
        bt_subscription = findViewById(R.id.bt_subscription);
        bt_subscription.setOnClickListener(this);
        bt_message.setOnClickListener(this);
        tv_message.setText("MainActivity");
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMoonEvent(EventBus eventBus){
        tv_message.setText(eventBus.getMessage());
    }

    @Override
    public void onClick(View view) {
        if(view.getId()==R.id.bt_message){
            startActivity(new Intent(MainActivity.this,SecondActivity.class));
        }
        else if(view.getId()==R.id.bt_subscription){
            if(!org.greenrobot.eventbus.EventBus.getDefault().isRegistered(this)) {
                org.greenrobot.eventbus.EventBus.getDefault().register(MainActivity.this);
            }
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        org.greenrobot.eventbus.EventBus.getDefault().unregister(this);

    }
}

其中

  if(!org.greenrobot.eventbus.EventBus.getDefault().isRegistered(this)) {
                org.greenrobot.eventbus.EventBus.getDefault().register(MainActivity.this);
            }

if语句是判断该订阅事件是否在这个Activity里面注册过,要是没有的话就执行

xxxx.getDefault().register();

进行订阅事件的注册

要是没有

 if(!org.greenrobot.eventbus.EventBus.getDefault().isRegistered(this))

你注册两次会报一个错误

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.eventbus_pratice, PID: 12132
org.greenrobot.eventbus.EventBusException: Subscriber class com.example.eventbus_pratice.MainActivity already registered to event class com.example.eventbus_pratice.EventBus
at org.greenrobot.eventbus.EventBus.subscribe(EventBus.java:168)
at org.greenrobot.eventbus.EventBus.register(EventBus.java:153)
at com.example.eventbus_pratice.MainActivity.onClick(MainActivity.java:42)
at android.view.View.performClick(View.java:7125)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1131)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access 3500 ( V i e w . j a v a : 801 ) a t a n d r o i d . v i e w . V i e w 3500(View.java:801) at android.view.View 3500(View.java:801)atandroid.view.ViewPerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

然后在onDestroy()里面注销

 @Override
    protected void onDestroy() {
        super.onDestroy();
        org.greenrobot.eventbus.EventBus.getDefault().unregister(this);
    }


这段代码是事件订阅者处理事件

  @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMoonEvent(EventBus eventBus){
        tv_message.setText(eventBus.getMessage());
    }



第二个Activity

public class SecondActivity extends AppCompatActivity {
    private Button register;
    private Button bt_sticky;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        register = findViewById(R.id.register);
        bt_sticky = findViewById(R.id.bt_sticky);
        register.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EventBus.getDefault().post(new com.example.eventbus_pratice.EventBus("事件发送成功!!!"));
                finish();
            }
        });
    }
}

下面这段代码是事件发布者发布事件

EventBus.getDefault().post(new com.example.eventbus_pratice.EventBus("事件发送成功!!!"));




在第一个ActivityTextViewMainActivity,先按注册那个按钮,再进入SecondActivity按下发送那个按钮你会发现MainActivityTextView变成了事件发送成功!!!

这就替代了之前第二个Activity给第一个Activity传递数据的方法

那么还有一个事情,如果我想先让第二个Activity给第一个Activity传递数据之后,我第一个Activity再按注册那个按钮可以吗,

答案是:这个代码是不行的,但是稍微改一改就可以了.

下面将讲到EventBus的黏性事件

4.EventBus的黏性事件

首先先改发送事件那块吧

SecondActivity中:

EventBus.getDefault().postSticky(new com.example.eventbus_pratice.EventBus("黏性事件发送成功!!!"));

之前我们的是:

xxx.post();

但是现在改成了:

xxx.postSticky();

然后是处理发送事件那块

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void ononMoonStickyEvent(EventBus eventBus){
    tv_message.setText(eventBus.getMessage());
}

原先反射那块是:

@Subscribe(threadMode = ThreadMode.MAIN)

现在要在后面多加一个

,sticky = true

现在我们就可以做到先发送事件,后注册了。

5.EventBus的源码

EventBus的构造方法

getDefault()源码

public static EventBus getDefault() {
    EventBus instance = defaultInstance;
    if (instance == null) {
        synchronized (EventBus.class) {
            instance = EventBus.defaultInstance;
            if (instance == null) {
                instance = EventBus.defaultInstance = new EventBus();
            }
        }
    }
    return instance;
}

用的DCL单例模式,确保缺省状态下EventBus的实例只有一个,现在

我们想要知道EventBus这个类下的instance是怎么创建的,就得点击**new EventBus()**查看它里面的源码了

EventBus()源码

public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    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;
}

这段代码的主要作用是初始化EventBus实例,并为其设置必要的参数和成员变量,以便后续在使用EventBus时能够正常工作。其中,包含了创建各种Poster、Finder和Executor的实例,并将它们赋值给相应的变量,这些实例在EventBus的后续使用中起到了关键作用。

订阅者注册

register()源码

public void register(Object subscriber) {
    if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) {
        // Crash if the user (developer) has not imported the Android compatibility library.
        throw new RuntimeException("It looks like you are using EventBus on Android, " +
                "make sure to add the \"eventbus\" Android library to your dependencies.");
    }

    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

首先

AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()

if语句的这段代码是用来检查当前是否在Android环境下,并且是否导入了EventBus库的Android兼容库。如果未导入,将会抛出异常。


  Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

首先是获得subscriber对象的Class对象,并使用subscriberMethodFinder对象查找该对象中所有的订阅者方法,返回一个SubscriberMethod列表。


 synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }

在同步块中,for循环遍历了subscriberMethods列表中的每一个SubscriberMethod对象,然后调用了subscribe方法将该SubscriberMethod对象中的订阅者方法添加到订阅列表中。并完成了订阅者的注册(订阅者的注册是通过调用subscribe方法实现的)

所以可以看出来register方法主要实现了2个作用:

  1. 查找订阅者的订阅方法
  2. 订阅者的注册

SubscriberMethod类中主要用来保存订阅方法的Method对象,线程模式,事件类型,优先级,是否为黏性事件

刚才我们说了findSubscriberMethods()是用来查找subscriberClass中所有的订阅者方法,现在我们看看里面的源码

findSubscriberMethods()源码

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

逐一解析:

 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);

这段代码主要是从从缓存中获取给定类的SubscriberMethod列表。

然后下面的if语句则是对获得的SubscriberMethod列表进行判断

  if (subscriberMethods != null) {
        return subscriberMethods;
    }

如果可以从缓存中找到SubscriberMethod列表,那就把这个列表返回,结束操作,如果没有,则进行下面的操作


先了解一下下面那个if语句中的ignoreGeneratedIndex的作用

点击ignoreGeneratedIndex会发现

private final boolean ignoreGeneratedIndex;

ignoreGeneratedIndex是一个 boolean类型的实例变量

虽然在代码中,ignoreGeneratedIndex被声明为private final boolean类型,但它并不是一个常量。

在Java中,final关键字有两种使用方式:一种是用于声明常量,即一旦初始化后就不能被重新赋值的变量;另一种是用于声明不可变的变量,即变量的值在初始化之后不能被修改。

在上述代码中,虽然ignoreGeneratedIndex被声明为final,但它的值可以在构造函数中或者通过setIgnoreGeneratedIndex方法进行修改,因此它不是一个常量。

相反,ignoreGeneratedIndex是一个实例变量,它的值在实例化对象之后可以被修改。final关键字的使用可以保证在实例化对象之后,ignoreGeneratedIndex的值不会被再次修改,即保证了它的不可变性,但是它不满足常量的定义,因为它的值可以在实例化之前被修改。

因此,虽然ignoreGeneratedIndex被声明为private final boolean类型,但它不是一个常量。

一般用ignoreGeneratedIndex属性的值选择用何种方法查找订阅方法的集合,表示是否忽略注释器生成的MyEventBusIndex

默认ignoreGeneratedIndex的值为false

如果ignoreGeneratedIndextrue执行

 subscriberMethods = findUsingReflection(subscriberClass);

用于通过反射机制查找给定类中带有@Subscribe注解的方法,并将它们封装成SubscriberMethod对象的列表。

如果为false则执行

 subscriberMethods = findUsingInfo(subscriberClass);

接下来看

 if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }

如果subscriberMethods为空则抛出一个注释

如果不为空则放入缓存供下次使用。


现在我们再看看当ignoreGeneratedIndex为false的时候**,findUsingInfo(subscriberClass)**干了些什么

findUsingInfo()源码
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
      	 findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();//用于将当前正在查找的类设置为其父类。
    }
    return getMethodsAndRelease(findState);
}

其中

  FindState findState = prepareFindState();

FindState来保存查找的状态

然后调用

 findState.initForSubscriber(subscriberClass);

来初始化查找的状态

然后如果当前保存的查找的类不为空的话就进入while循环

findState调用subscriberInfo的getSubscriberMethods()方法将值传递给SubscriberMethod

然后遍历SubscriberMethod数组,,对于每一个SubscriberMethod对象,执行以下操作:

  • 调用findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)方法,判断subscriberMethod是否符合添加的条件。checkAdd方法的作用是:如果findState.checkMethod是否为true,则检查方法签名是否匹配;否则,只检查事件类型是否匹配。
  • 如果checkAdd方法返回true,则将subscriberMethod添加到findState.subscriberMethods列表中。

如果findState.subscriberInfo(即:如果当前保存的查找的类为空)

则调用:

findUsingReflectionInSingleClass(findState);

用于在单个类中使用反射查找并添加SubscriberMethod对象。

查看一下它的源码

findUsingReflectionInSingleClass()源码
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        try {
            methods = findState.clazz.getMethods();
        } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
            String msg = "Could not inspect methods of " + findState.clazz.getName();
            if (ignoreGeneratedIndex) {
                msg += ". Please consider using EventBus annotation processor to avoid reflection.";
            } else {
                msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
            }
            throw new EventBusException(msg, error);
        }
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException("@Subscribe method " + methodName +
                        "must have exactly 1 parameter but has " + parameterTypes.length);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

这么长的代码我们只用关注一个:

 methods = findState.clazz.getDeclaredMethods();

通过反射,获取订阅者所有的方法,根据方法的类型,参数,注解找到订阅方法,并将订阅方法信息保存到findState中

findSubscriberMethods()流程图操作

在这里插入图片描述

subscribe()源码

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    if (subscriberMethod.sticky) {
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

首先

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

会根据

subscriber(订阅者)subscriberMethod(订阅方法)创建一个Subscription(订阅对象)

然后在

 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

根据eventType获得subscriptions集合


如果之前根据subscriptions为空

那个集合就重新创建,并根据eventType保存在subscriptionsByEventType(Map集合)

 if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    }

然后在

  int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

根据订阅方法的优先级。将新的订阅对象插入到订阅对象集合中去。


 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);

通过subscriber(订阅者)获取subscribedEvents(事件类型集合)

如果subscribedEvents为空则重新创建,再将subscriber, subscribedEvents传给typesBySubscriber

 if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }

 if (subscriberMethod.sticky) {
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }

如果是黏性事件,则从stickyEvents事件保存队列中取出该事件类型的事件发送给当前订阅者

具体来说,subscribe方法做了三件事情:

  1. 维护了当前EventBus中的subscriptionsByEventType表;(将Subscriptions根据eventType封装到subscriptionsByEventType中)
  2. 维护了当前EventBus中的 typesBySubscriber表;(将subscribedEvents根据subscriber封装到 typesBySubscriber)
  3. 处理了粘性事件;

第一个表是代表消息事件类型和Subscription关联的一个表,
第二个表是表示订阅者Subscriber和消息事件类型关联的两个表,这两个表可以在发布事件时可以快速地找到对应的订阅者并调用其处理方法。

事件发送

就只看post()了,postSticky()就不看了

public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    //保存事件队列和线程状态信息
    
    List<Object> eventQueue = postingState.eventQueue;
    //获取事件队列
    
    eventQueue.add(event);
    //将事件插入队列中

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            //处理队列中所有事件
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

先创建一个PostingThreadState用来保存事件队列和线程状态信息,然后从该对象中取出事件队列,将事件插入该队列中。

后面进行处理

而处理主要是在**postSingleEvent()**执行的

现在看看**postSingleEvent()**的源码

postSingleEvent()源码

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

eventInheritance为是否向上查找的父类,默认为true

可通过EventBusBuilder配置,为true时,通过lookupAllEventTypes找到所有的父类并存在集合中,然后用postSingleEventForEventType逐一处理

看看postSingleEventForEventType的源码

postSingleEventForEventType()源码
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

subscriptions = subscriptionsByEventType.get(eventClass);

取出该事件对应的Subscriptions(订阅对象集合)然后遍历该集合.将event与subscription传递给postingState然后在postToSubscription()处理

看看这个方法的源码

postToSubscription()源码
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

它是先取出订阅方法的线程模式然后分别处理

订阅者取消注册

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

先从typesBySubscriber Map集合中拿取subscribedTypes(事件类型集合)在不等于null情况下遍历将subscriber(订阅者)移除掉。

而它上面的代码

unsubscribeByEventType(subscriber, eventType);

用于取消订阅者订阅某个特定类型的事件

看看里面的源码

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions != null) {
        int size = subscriptions.size();
        for (int i = 0; i < size; i++) {
            Subscription subscription = subscriptions.get(i);
            if (subscription.subscriber == subscriber) {
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}

通过eventType得到对应的subscriptions并在for循环判断该订阅集合(subscriptions)对应的订阅对象(subscriber)的属性与传进来的是否相等,一样的话就移除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/672097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP的三次握手与四次挥手

TCP的三次握手与四次挥手 1.网络分层 网络分层代表硬件协议/技术特性应用层HTTP,DNS,FTP,SMTP,Telnet协议等应用程序实现的,规定应用程序的数据格式传输层TCP/UDP协议负责两主机之间的数据正确传输主机系统内核实现的网络层路由器IP协议负责地址管理和路由选择(确定对应主机)…

合宙Air724UG Cat.1模块硬件设计指南--SDIO接口

SDIO接口 简介 SDIO(Secure Digital Input and Output)全称为安全数字输入输出接口&#xff0c;在协议上和SPI类似是一种串行的硬件接口&#xff0c;通信的双方一个作为HOST&#xff0c;另一端是Device&#xff0c;所有的通信都是由HOST端发送命令开始的&#xff0c;Device端只…

SpringCloud Alibaba入门3之nacos服务搭建

在前一章的基础上开发:SpringCloud Alibaba入门之用户子模块开发_qinxun2008081的博客-CSDN博客 一、下载nacos-server 从https://github.com/alibaba/nacos/releasesopen in new window 下载nacos-server发行版。 二、启动nacos 进入%path%\nacos\bin文件夹,执行cmd命令st…

阿里组织变革新阶段:蓄力拉弓,一箭向前

自3月28日宣布“16N”分拆、5月18日宣布分业务启动独立融资或上市计划以来&#xff0c;阿里持续推动着这场史无前例的组织变革落地&#xff0c;谋求更高质量发展。 6月20日&#xff0c;阿里巴巴控股集团董事会主席兼CEO张勇通过全员信宣布&#xff0c;他将于今年9月10日卸任现…

[进阶]网络通信:端口和协议

端口 标记正在计算机设备上运行的应用程序的&#xff0c;被规定为一个 16位的二进制&#xff0c;范围是 0~65535。 分类 周知端口&#xff1a;0~1023&#xff0c;被预先定义的知名应用占用&#xff08;如&#xff1a;HTTP占用 80&#xff0c;FTP占用21&#xff09;注册端口&…

java中抽象类和抽象方法

文章目录 前言 一、抽象类和抽象方法是什么&#xff1f; 1.抽象类 2.抽象方法 二、使用方法 1.实操展示 2.注意事项 总结 前言 苹果这个具体的水果&#xff0c;它具有的属性为&#xff0c;红色&#xff1b;它具有的方法为&#xff0c;被啃。那么&#xff0c;水果&#…

system Verilog 验证测试平台编写指南——读书笔记(持续更新)

第一章 验证导论 1、基本测试平台的功能 测试平台的用途在于确定待测设计的正确性。包含下列步骤&#xff1a; &#xff08;1&#xff09;产生激励。 &#xff08;2&#xff09;把激励施加到DUT上. &#xff08;3&#xff09;捕捉响应。 &#xff08;4&#xff09;检验正…

【Visual Studio】开发 Qt 时右键没有自动添加 slots 槽的功能,使用 C++ 语言,配合 Qt 开发串口通信界面

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 Ref. 基于 Visual Studio 环境下使用 Qt&#xff0c;发现右键没有自动添加槽的功能&#xff0c;需要自己想办法。我看网上介绍了不止一种方法&#…

Micrometer实战

Micrometer 为基于 JVM 的应用程序的性能监测数据收集提供了一个通用的 API&#xff0c;支持多种度量指标类型&#xff0c;这些指标可以用于观察、警报以及对应用程序当前状态做出响应。 前言 可接入监控系统 监控系统的三个重要特征&#xff1a; 维度&#xff08;Dimensio…

事务的历史与SSI——PostgreSQL数据库技术峰会成都站分享

前言 PostgreSQL数据库技术峰会成都站 近期&#xff08;2023年6月17日&#xff09;&#xff0c;由中国开源软件推进联盟PG分会发起的“PostgreSQL数据库技术峰会成都站”圆满举行。我也有幸作为演讲嘉宾参加了此次峰会&#xff0c;收获很多。 &#xff08;分会回顾和所有pp…

Qt编写监控实时显示和取流回放工具(回放支持切换进度)

一、前言 现在各个监控大厂做的设备&#xff0c;基本上都会支持通过rtsp直接取流显示&#xff0c;而且做的比较好的还支持通过rtsp回放取流&#xff0c;基本上都会约定一个字符串的规则&#xff0c;每个厂家都是不一样的规则&#xff0c;比如回放对应的rtsp地址还要带上时间范…

Java8 List集合如何指定打印分隔符

目录 背景方法一&#xff1a;String.join&#xff08;推荐&#xff09;方法二&#xff1a;Collectors.joining总结 背景 无论是在学习还是日常的应用开发过程中&#xff0c;我们经常会需要使用分隔符将 List 集合打印出来。 如下所示&#xff1a; import java.util.Arrays;pub…

数据结构与算法基础(青岛大学-王卓)(5)

叮叮咚咚&#xff0c;新一期来袭&#xff0c;我还在吃桃子&#xff0c;吃桃子&#xff0c;吃桃子。。。串和python的字符串差不多,数组和广义表像是python的list 文章目录 串(string) - 字符串概念及术语串的类型定义存储结构&#xff08;同线性表&#xff09;串的模式匹配算法…

leetcode46. 全排列(回溯算法-java)

全排列 leetcode46. 全排列题目描述解题思路代码演示 回溯算法专题 leetcode46. 全排列 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/permutations 题目描述 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有…

关于平差中误差方程 v=Bx-l 的探讨

文章目录 Part.I IntroductionPart.II 误差方程的探讨Chap.I 符号表示Chap.II 误差方程的含义Chap.III 误差方程的其他形式Chap.IV 平差的大致流程 Part.III 误差方程的表示形式 Part.I Introduction 在平时阅读文献或者整理笔记时&#xff0c;经常会看到各种各样的有关误差方…

Git安装详细教程(win11)

Git安装详细教程&#xff08;win11&#xff09; 一、下载二、安装三、配置 一、下载 官网下载&#xff1a;点击下载 网盘下载&#xff1a;点击下载 二、安装 双击程序运行&#xff0c;点击next 选择安装路径&#xff0c;我安装在了D盘&#xff0c;如下图所示&#xff0c;…

Server - 测试 GPU 的显卡使用率与张量之间的关系

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/131331049 NVIDIA A100 是一款基于 Ampere 架构的高性能 GPU&#xff0c;专为 AI、数据分析和高性能计算等应用场景设计。NVIDIA A100 具…

【Java入门基础第10天】Java程序结构

Java程序结构 1、Java程序结构什么是Java程序结构&#xff1f; 2、Java注释1、单行注释2、多行注释3、多行注释 1、Java程序结构 什么是Java程序结构&#xff1f; 程序是由程序块组成的&#xff0c;所谓程序块&#xff0c;指的是使用一对{ } 包含若干的代码的总称。程序块是由…

【Python 基础篇】Python 变量与数据类型以及数据类型转换

文章目录 引言一、变量和常见数据类型1. 变量2. 常见数据类型 二、数据类型转换结论 引言 Python 是一种广泛应用于各个领域的高级编程语言&#xff0c;其灵活性和易用性使其成为众多开发者的首选。在 Python 中&#xff0c;变量是程序中存储数据的基本单元&#xff0c;而数据…

数据结构学习笔记:概论

✨博文作者&#xff1a;烟雨孤舟 &#x1f496; 喜欢的可以 点赞 收藏 关注哦~~ ✍️ 作者简介: 一个热爱大数据的学习者 ✍️ 笔记简介&#xff1a;作为大数据爱好者&#xff0c;以下是个人总结的学习笔记&#xff0c;如有错误&#xff0c;请多多指教&#xff01; 目录 数据结…