当前位置: 首页 > news >正文

【Android】EventBus的使用及源码分析

文章目录

  • 介绍
    • 优点
    • 基本用法
    • 线程模式
      • POSTING
      • MAIN
      • MAIN_ORDERED
      • BACKGROUND
      • ASYNC
    • 黏性事件
  • 源码
    • 注册
      • getDefault()
      • register
      • findSubscriberMethods
      • 小结
    • post
    • postSticky
    • unregister

介绍

img

优点

  • 简化组件之间的通信
    • 解耦事件发送者和接收者
    • 在 Activity、Fragment 和后台线程中表现良好
    • 避免复杂且容易出错的依赖关系和生命周期问题
  • 让你的代码更简单
  • 很快,很小
  • 具有高级功能,如交付线程、订阅者优先级等。

基本用法

导入依赖

implementation "org.greenrobot:eventbus:3.3.1"
  1. 定义事件:
public static class MessageEvent { /* Additional fields if needed */ }
  1. 准备订阅者:声明并注释您的订阅方法,可以选择指定线程模式

    @Subscribe(threadMode = ThreadMode.MAIN)  
    public void onMessageEvent(MessageEvent event) {// Do something
    }
    

    注册和取消注册您的订户。例如在 Android 上,活动和片段通常应根据其生命周期进行注册:

     @Overridepublic void onStart() {super.onStart();EventBus.getDefault().register(this);}@Overridepublic void onStop() {super.onStop();EventBus.getDefault().unregister(this);}
    
  2. 发布活动:

 EventBus.getDefault().post(new MessageEvent());

线程模式

POSTING

  • 特点:订阅者在发布事件的同一线程中被调用。
  • 优点:开销最小,避免了线程切换。
  • 适用场景:已知任务简单且快速完成,不依赖主线程。
  • 注意:长时间任务可能阻塞发布线程(如是主线程,会导致UI卡顿)。
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {log(event.message); // 快速返回的简单任务
}

MAIN

  • 特点:

    • 订阅者在主线程(UI线程)中被调用
    • 如果发布线程为主线程,则同步调用(与 POSTING 类似)
  • 适用场景:UI更新或需要在主线程完成的轻量任务。

  • 注意:避免执行耗时任务,否则会阻塞主线程,导致卡顿。

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {textView.setText(event.message); // 更新UI
}

MAIN_ORDERED

  • 特点:
    1. 在主线程中执行。
    2. 按顺序执行:事件会一个接一个地处理,不会乱序。
  • 适用场景:依赖特定执行顺序的UI更新逻辑。
  • 注意:与 MAIN 类似,避免耗时任务,确保任务快速返回。
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessageEvent(String event) {Log.d("EventBus", "Event received: " + event); // 按顺序更新UI
}

BACKGROUND

  • 特点:
    • 如果发布线程为主线程,事件处理方法会切换到后台线程。
    • 如果发布线程是非主线程,事件处理方法直接在发布线程中执行。
  • 适用场景:后台任务,如数据库存储、文件操作。
  • 注意:快速返回,避免阻塞后台线程。
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event) {saveToDisk(event.message); // 后台存储操作
}

ASYNC

  • 特点:事件处理程序始终在独立线程中调用,与发布线程或主线程完全分离。
  • 适用场景:耗时操作,如网络请求、复杂计算。
  • 注意:避免触发大量异步任务,防止线程池耗尽资源。
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event) {backend.send(event.message); // 异步网络请求
}

黏性事件

发送事件之后再订阅也能收到该事件

@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void onMessageEvent(MessageEvent messageEvent) {tv.setText(messageEvent.getMessage());
}
EventBus.getDefault().postSticky(new MessageEvent("SecondActivity的信息"));

源码

注册

getDefault()

public class EventBus {// 静态变量,存储唯一的 EventBus 实例// 使用 volatile 关键字,确保多线程环境下变量的可见性和防止指令重排static volatile EventBus defaultInstance;public static EventBus getDefault() {// 将静态变量 defaultInstance 赋值给局部变量 instance,减少对主内存的访问EventBus instance = defaultInstance;// 第一次检查,避免不必要的同步开销if (instance == null) {// 如果实例未被初始化,进入同步块synchronized (EventBus.class) {// 再次将 defaultInstance 的值赋给 instance(看这个时候defaultInstance为不为空)instance = EventBus.defaultInstance;// 第二次检查,确保实例仍未被初始化(双重检查锁定)if (instance == null) {// 创建新的 EventBus 实例并赋值给 defaultInstance 和局部变量 instanceinstance = EventBus.defaultInstance = new EventBus();}}}return instance;}
}
public EventBus() {this(DEFAULT_BUILDER);
}
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
EventBus(EventBusBuilder builder) {//日志logger = builder.getLogger();//这个集合可以根据事件类型获取订阅者//key:事件类型,value:订阅该事件的订阅者集合subscriptionsByEventType = new HashMap<>();//订阅者所订阅的事件集合//key:订阅者,value:该订阅者订阅的事件集合typesBySubscriber = new HashMap<>();//粘性事件集合//key:事件Class对象,value:事件对象stickyEvents = new ConcurrentHashMap<>();//Android主线程处理事件mainThreadSupport = builder.getMainThreadSupport();mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;//Background事件发送者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时最终会调用它的有参构造函数,传入一个EventBus.Builder对象。在这个有参构造函数内部对属性进行初始化

register

public class EventBus {public void register(Object subscriber) {// 1、通过反射获取到订阅者的Class对象Class<?> subscriberClass = subscriber.getClass();// 2、通过subscriberMethodFinder对象获取订阅者所订阅事件的集合List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);synchronized (this) {// 3、遍历集合进行注册for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);}}}private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {// 4、获取事件类型Class<?> eventType = subscriberMethod.eventType;// 5、封装Subscription对象Subscription newSubscription = new Subscription(subscriber, subscriberMethod);// 6、通过事件类型获取该事件的订阅者集合CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);// 7、如果没有订阅者订阅该事件if (subscriptions == null) {// 创建集合,存入subscriptionsByEventType集合中subscriptions = new CopyOnWriteArrayList<>();subscriptionsByEventType.put(eventType, subscriptions);} else { // 8、如果有订阅者已经订阅了该事件// 判断这些订阅者中是否有重复订阅的现象if (subscriptions.contains(newSubscription)) {throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);}}int size = subscriptions.size();// 9、遍历该事件的所有订阅者for (int i = 0; i <= size; i++) {// 按照优先级高低进行插入,如果优先级最低,插入到集合尾部if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {subscriptions.add(i, newSubscription);break;}}// 10、获取该事件订阅者订阅的所有事件集合List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {subscribedEvents = new ArrayList<>();typesBySubscriber.put(subscriber, subscribedEvents);}// 11、将该事件加入到集合中subscribedEvents.add(eventType);// 12、判断该事件是否是粘性事件if (subscriberMethod.sticky) {if (eventInheritance) { // 13、判断事件的继承性,默认是不可继承// 14、获取所有粘性事件并遍历,判断继承关系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();// 15、调用checkPostStickyEventToSubscription方法checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}} else {Object stickyEvent = stickyEvents.get(eventType);checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}}private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {if (stickyEvent != null) {// 16、如果粘性事件不为空postToSubscription(newSubscription, stickyEvent, isMainThread());}}private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {// 17、根据threadMode的类型去选择是直接反射调用方法,还是将事件插入队列switch (subscription.subscriberMethod.threadMode) {case POSTING:invokeSubscriber(subscription, event);break;case MAIN:if (isMainThread) {// 18、通过反射的方式调用invokeSubscriber(subscription, event);} else {// 19、将粘性事件插入到队列中// 最后还是会调用EventBus.invokeSubscriber(PendingPost pendingPost)方法。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 subscriberinvokeSubscriber(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);}}void invokeSubscriber(PendingPost pendingPost) {Object event = pendingPost.event;Subscription subscription = pendingPost.subscription;PendingPost.releasePendingPost(pendingPost);if (subscription.active) {invokeSubscriber(subscription, event);}}void invokeSubscriber(Subscription subscription, Object event) {try {subscription.subscriberMethod.method.invoke(subscription.subscriber, event);} catch (InvocationTargetException e) {handleSubscriberException(subscription, event, e.getCause());} catch (IllegalAccessException e) {throw new IllegalStateException("Unexpected exception", e);}}
}public class SubscriberMethod {final Method method; // 处理事件的Method对象final ThreadMode threadMode; //线程模型final Class<?> eventType; //事件类型final int priority; //事件优先级final boolean sticky; //是否是粘性事件String methodString;
}final class Subscription {final Object subscriber;final SubscriberMethod subscriberMethod;
}

findSubscriberMethods

class SubscriberMethodFinder {private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();private static final int POOL_SIZE = 4;private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {// 1、先从之前缓存的集合中获取List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);if (subscriberMethods != null) {// 2、如果之前缓存了,直接返回return subscriberMethods;}if (ignoreGeneratedIndex) { //ignoreGeneratedIndex一般为falsesubscriberMethods = findUsingReflection(subscriberClass);} else {// 3、获取所有订阅方法集合subscriberMethods = findUsingInfo(subscriberClass);}if (subscriberMethods.isEmpty()) {throw new EventBusException("Subscriber " + subscriberClass+ " and its super classes have no public methods with the @Subscribe annotation");} else {// 4、放入缓存集合中METHOD_CACHE.put(subscriberClass, subscriberMethods);return subscriberMethods;}}private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {// 5、从数组中获取FindState对象// 如果有直接返回,如果没有创建一个新的FindState对象FindState findState = prepareFindState();// 6、根据事件订阅者初始化findStatefindState.initForSubscriber(subscriberClass);while (findState.clazz != null) {// 7、获取subscriberInfo,初始化为nullfindState.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 {// 8、通过反射的方式获取订阅者中的Method,默认情况findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}private FindState prepareFindState() {synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {FindState state = FIND_STATE_POOL[i];if (state != null) {FIND_STATE_POOL[i] = null;return state;}}}return new FindState();}private void findUsingReflectionInSingleClass(FindState findState) {Method[] methods;try {// 9、订阅者中所有声明的方法,放入数组中methods = findState.clazz.getDeclaredMethods();} catch (Throwable th) {// 10、获取订阅者中声明的public方法,设置跳过父类methods = findState.clazz.getMethods();findState.skipSuperClasses = true;}// 遍历这些方法for (Method method : methods) {// 11、获取方法的修饰符:public、private等等int modifiers = method.getModifiers();// 12、订阅方法为public同时不是abstract、staticif ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {// 13、方法参数类型数组Class<?>[] parameterTypes = method.getParameterTypes();if (parameterTypes.length == 1) {// 14、获取方法的注解Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);// 15、如果有注解if (subscribeAnnotation != null) {Class<?> eventType = parameterTypes[0];// 16、将method和eventType放入到findState进行检查if (findState.checkAdd(method, eventType)) {// 17、获取注解中的threadMode对象ThreadMode threadMode = subscribeAnnotation.threadMode();// 18、新建一个SubscriberMethod对象,同时加入到findState中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");}}}// 从findState中获取订阅者所有方法并释放private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {// 获取订阅者所有订阅方法集合List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);// findState进行回收findState.recycle();synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {if (FIND_STATE_POOL[i] == null) {FIND_STATE_POOL[i] = findState;break;}}}// 返回集合return subscriberMethods;}
}

小结

  1. 根据单例设计模式创建一个EventBus对象,同时创建一个EventBus.Builder对象对EventBus进行初始化,其中有三个比较重要的集合和一个SubscriberMethodFinder对象。
  2. 调用register方法,首先通过反射获取到订阅者的Class对象。
  3. 通过SubscriberMethodFinder对象获取订阅者中所有订阅的事件集合,它先从缓存中获取,如果缓存中有,直接返回;如果缓存中没有,通过反射的方式去遍历订阅者内部被注解的方法,将这些方法放入到集合中进行返回。
  4. 遍历第三步获取的集合,将订阅者和事件进行绑定。
  5. 在绑定之后会判断绑定的事件是否是粘性事件,如果是粘性事件,直接调用postToSubscription方法,将之前发送的粘性事件发送给订阅者。

post

public class EventBus {...public void post(Object event) {// 1、获取当前线程的PostingThreadState,这是一个ThreadLocal对象PostingThreadState postingState = currentPostingThreadState.get();// 2、当前线程的事件集合List<Object> eventQueue = postingState.eventQueue;// 3、将要发送的事件加入到集合中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 {// 4、只要事件集合中还有事件,就一直发送while (!eventQueue.isEmpty()) {postSingleEvent(eventQueue.remove(0), postingState);}} finally {postingState.isPosting = false;postingState.isMainThread = false;}}}// currentPostingThreadState是包含了PostingThreadState的ThreadLocal对象// ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据, 并且线程之间的数据是相互独立的。// 其内部通过创建一个它包裹的泛型对象的数组,不同的线程对应不同的数组索引,每个线程通过get方法获取对应的线程数据。private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {@Overrideprotected PostingThreadState initialValue() {return new PostingThreadState();}};// 每个线程中存储的数据final static class PostingThreadState {final List<Object> eventQueue = new ArrayList<>(); // 线程的事件队列boolean isPosting; //是否正在发送中boolean isMainThread; //是否在主线程中发送Subscription subscription; //事件订阅者和订阅事件的封装Object event; //事件对象boolean canceled; //是否被取消发送}...private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {// 5、获取事件的Class对象Class<?> eventClass = event.getClass();boolean subscriptionFound = false;if (eventInheritance) { // eventInheritance一般为true// 6、 找到当前的event的所有 父类和实现的接口 的class集合List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);int countTypes = eventTypes.size();for (int h = 0; h < countTypes; h++) {Class<?> clazz = eventTypes.get(h);// 7、遍历集合发送单个事件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));}}}private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {synchronized (eventTypesCache) {// 获取事件集合List<Class<?>> eventTypes = eventTypesCache.get(eventClass);if (eventTypes == null) { //如果为空eventTypes = new ArrayList<>();Class<?> clazz = eventClass;while (clazz != null) {eventTypes.add(clazz); //添加事件addInterfaces(eventTypes, clazz.getInterfaces()); //添加当前事件的接口classclazz = clazz.getSuperclass();// 获取当前事件的父类}eventTypesCache.put(eventClass, eventTypes);}return eventTypes;}}//循环添加当前事件的接口classstatic void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {for (Class<?> interfaceClass : interfaces) {if (!eventTypes.contains(interfaceClass)) {eventTypes.add(interfaceClass);addInterfaces(eventTypes, interfaceClass.getInterfaces());}}}private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) {// 8、根据事件获取所有订阅它的订阅者subscriptions = subscriptionsByEventType.get(eventClass);}if (subscriptions != null && !subscriptions.isEmpty()) {// 9、遍历集合for (Subscription subscription : subscriptions) {postingState.event = event;postingState.subscription = subscription;boolean aborted = false;try {// 10、将事件发送给订阅者postToSubscription(subscription, event, postingState.isMainThread);aborted = postingState.canceled;} finally {// 11、重置postingStatepostingState.event = null;postingState.subscription = null;postingState.canceled = false;}if (aborted) {break;}}return true;}return false;}private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {// 12、根据订阅方法的线程模式调用订阅方法switch (subscription.subscriberMethod.threadMode) {case POSTING: //默认类型,表示发送事件操作直接调用订阅者的响应方法,不需要进行线程间的切换invokeSubscriber(subscription, event);break;case MAIN: //主线程,表示订阅者的响应方法在主线程进行接收事件if (isMainThread) { //如果发送者在主线程invokeSubscriber(subscription, event);//直接调用订阅者的响应方法} else { //如果事件的发送者不是主线程//添加到mainThreadPoster的队列中去,在主线程中调用响应方法mainThreadPoster.enqueue(subscription, event); }break;case MAIN_ORDERED:// 主线程优先模式if (mainThreadPoster != null) {mainThreadPoster.enqueue(subscription, event);} else {//如果不是主线程就在消息发送者的线程中进行调用响应方法invokeSubscriber(subscription, event);}break;case BACKGROUND:if (isMainThread) {// 如果事件发送者在主线程,加入到backgroundPoster的队列中,在线程池中调用响应方法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);}}...
}
  1. 获取当前线程的事件集合,将要发送的事件加入到集合中。
  2. 通过循环,只要事件集合中还有事件,就一直发送。
  3. 获取事件的Class对象,找到当前的event的所有父类和实现的接口的class集合。遍历这个集合,调用发送单个事件的方法进行发送。
  4. 根据事件获取所有订阅它的订阅者集合,遍历集合,将事件发送给订阅者。
  5. 发送给订阅者时,根据订阅方法的线程模式调用订阅方法,如果需要线程切换,则切换线程进行调用;否则,直接调用。

postSticky

EventBus.getDefault().postSticky(Object event)

public class EventBus {public void postSticky(Object event) {synchronized (stickyEvents) {// 1、将事件添加到粘性事件集合中stickyEvents.put(event.getClass(), event);}// 2、发送事件post(event);}
}
  1. 将粘性事件加入到EventBus对象的粘性事件集合中,当有新的订阅者进入后,如果该订阅者订阅了该粘性事件,可以直接发送给订阅者。
  2. 将粘性事件发送给已有的事件订阅者。

unregister

EventBus.getDefault().unregister(Object subscriber)

解注册的方法。

public class EventBus {...public synchronized void unregister(Object subscriber) {// 1、获取订阅者订阅的所有事件List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);if (subscribedTypes != null) {// 2、遍历集合for (Class<?> eventType : subscribedTypes) {// 3、将该订阅者的从订阅该事件的所有订阅者集合中移除unsubscribeByEventType(subscriber, eventType);}// 4、将订阅者从集合中移除typesBySubscriber.remove(subscriber);} else {logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());}}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--;}}}}...
}

1、获取订阅者的所有订阅方法,遍历这些方法。然后拿到每个方法对应的所有订阅者集合,将订阅者从集合中移除。
2、移除订阅者中所有的订阅方法。


参考:

  1. greenrobot/EventBus(github.com)
  2. EventBus源码解析 - 掘金 (juejin.cn)

相关文章:

【Android】EventBus的使用及源码分析

文章目录 介绍优点基本用法线程模式POSTINGMAINMAIN_ORDEREDBACKGROUNDASYNC 黏性事件 源码注册getDefault()registerfindSubscriberMethods小结 postpostStickyunregister 介绍 优点 简化组件之间的通信 解耦事件发送者和接收者在 Activity、Fragment 和后台线程中表现良好避…...

【大数据学习 | Spark调优篇】Spark之内存调优

1. 内存的花费 1&#xff09;每个Java对象&#xff0c;都有一个对象头&#xff0c;会占用16个字节&#xff0c;主要是包括了一些对象的元信息&#xff0c;比如指向它的类的指针。如果一个对象本身很小&#xff0c;比如就包括了一个int类型的field&#xff0c;那么它的对象头实…...

Linux:文件系统inode

早期&#xff0c;存储文件的设备是磁盘&#xff08;当下的市场几乎都是SSD&#xff09;&#xff0c;但大家习惯的把它们都称为磁盘&#xff0c;磁盘是用来表示区分内存的存储设备。而在操作系统看来&#xff0c;这个存储设备的结构就是一个线性结构&#xff0c;这一点很重要。 …...

力扣难题解析

滑动窗口问题 76.最小覆盖子串 题目链接&#xff1a;76. 最小覆盖子串 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空…...

4.5-Channel 和 Flow:SharedFlow 和 StateFlow

文章目录 SharedFlow数据流的收集和事件订阅的区别launchIn() 和 shareIn() 的区别SharedFlow 与 Flow、Channel 的区别shareIn() 适用场景 shareIn() 的具体参数说明shareIn() 的 replay 参数shareIn() 的 started 参数WhileSubscribed() 的参数及适用场景 MutableSharedFlow、…...

Qt | TCP服务器实现QTcpServer,使用线程管理客户端套接字

点击上方"蓝字"关注我们 01、QTcpServer >>> QTcpServer 是 Qt 网络模块中的一个类,用于实现TCP服务器。它允许创建一个服务器,可以接受来自客户端的连接。QTcpServer 是事件驱动的,这意味着它将通过信号和槽机制处理网络事件。 常用函数 构造函数: QT…...

【提高篇】3.6 GPIO(六,寄存器介绍,下)

目录 2.3 输出速度寄存器OSPEEDR(GPIOx_OSPEEDR) (x = A..I) 2.4 上拉/下拉寄存器 (GPIOx_PUPDR) (x = A..I) 2.5 输入数据寄存器(IDR) 2.6 输出数据寄存器(ODR) 2.7 置位/复位寄存器(BSRR) 2.8 BSRR与ODR寄存器的区别 2.3 输出速度寄存器OSPEEDR(GPIOx_OSPEEDR) (…...

【AI】数据,算力,算法和应用(3)

三、算法 算法这个词&#xff0c;我们都不陌生。 从接触计算机&#xff0c;就知道有“算法”这样一个神秘的名词存在。象征着专业、权威、神秘、高难等等。 算法是一组有序的解决问题的规则和指令&#xff0c;用于解决特定问题的一系列步骤。算法可以被看作是解决问题的方法…...

深度学习笔记——生成对抗网络GAN

本文详细介绍早期生成式AI的代表性模型&#xff1a;生成对抗网络GAN。 文章目录 一、基本结构生成器判别器 二、损失函数判别器生成器交替优化目标函数 三、GAN 的训练过程训练流程概述训练流程步骤1. 初始化参数和超参数2. 定义损失函数3. 训练过程的迭代判别器训练步骤生成器…...

网络安全开源组件

本文只是针对开源项目进行收集&#xff0c;如果后期在工作中碰到其他开源项目将进行更新。欢迎大家在评论区留言&#xff0c;您在工作碰到的开源项目。 祝您工作顺利&#xff0c;鹏程万里&#xff01; 一、FW&#xff08;防火墙&#xff09; 1.1 pfSense pfSense项目是一个免费…...

Python毕业设计选题:基于django+vue的智慧社区可视化平台的设计与实现+spider

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 养老机构管理 业主管理 社区安防管理 社区设施管理 车位…...

Oracle LinuxR7安装Oracle 12.2 RAC集群实施(DNS解析)

oracleLinuxR7-U6系统Oracle 12.2 RAC集群实施&#xff08;DNS服务器&#xff09; 环境 RAC1RAC2DNS服务器操作系统Oracle LinuxR7Oracle LinuxR7windows server 2008R2IP地址172.30.21.101172.30.21.102172.30.21.112主机名称hefei1hefei2hefei数据库名hefeidbhefeidb实例名…...

M2芯片安装es的步骤

背景&#xff1a;因为最近经常用到es&#xff0c;但是测试环境没有es&#xff0c;自己本地也没安装&#xff0c;为了方便测试&#xff0c;然后安装一下&#xff0c;但是刚开始安装就报错&#xff0c;记录一下&#xff0c;安装的版本为8.16.1 第一步&#xff1a;去官网下载maco…...

macos下brew安装redis

首先确保已安装brew&#xff0c;接下来搜索资源&#xff0c;在终端输入如下命令&#xff1a; brew search redis 演示如下&#xff1a; 如上看到有redis资源&#xff0c;下面进行安装&#xff0c;执行下面的命令&#xff1a; brew install redis 演示效果如下&#xff1a; …...

第六届金盾信安杯-SSRF

操作内容&#xff1a; 进入环境 可以查询网站信息 查询环境url https://114.55.67.167:52263/flag.php 返回 flag 就在这 https://114.55.67.167:52263/flag.php 把这个转换成短连接&#xff0c;然后再提交 得出 flag...

【论文投稿】国产游戏技术:迈向全球引领者的征途

【IEEE出版南方科技大学】第十一届电气工程与自动化国际会议&#xff08;IFEEA 2024)_艾思科蓝_学术一站式服务平台 更多学术会议论文投稿请看&#xff1a;https://ais.cn/u/nuyAF3 目录 国产游戏技术能否引领全球&#xff1f; 一、国产游戏技术的崛起之路 1.1 初期探索与积…...

腾讯微众银行大数据面试题(包含数据分析/挖掘方向)面试题及参考答案

为什么喜欢使用 XGBoost,XGBoost 的主要优势有哪些? XGBoost 是一个优化的分布式梯度增强库,在数据科学和机器学习领域应用广泛,深受喜爱,原因主要在于其众多突出优势。 首先,它的精度高,在许多机器学习竞赛和实际应用中,XGBoost 都展现出卓越的预测准确性。其基于决策…...

【Linux】死锁、读写锁、自旋锁

文章目录 1. 死锁1.1 概念1.2 死锁形成的四个必要条件1.3 避免死锁 2. 读者写者问题与读写锁2.1 读者写者问题2.2 读写锁的使用2.3 读写策略 3. 自旋锁3.1 概念3.2 原理3.3 自旋锁的使用3.4 优点与缺点 1. 死锁 1.1 概念 死锁是指在⼀组进程中的各个进程均占有不会释放的资源…...

Spring Web开发(请求)获取JOSN对象| 获取数据(Header)

大家好&#xff0c;我叫小帅今天我们来继续Spring Boot的内容。 文章目录 1. 获取JSON对象2. 获取URL中参数PathVariable3.上传⽂件RequestPart3. 获取Cookie/Session3.1 获取和设置Cookie3.1.1传统获取Cookie3.1.2简洁获取Cookie 3. 2 获取和存储Session3.2.1获取Session&…...

用c语言完成俄罗斯方块小游戏

用c语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发&#xff0c;怎么说呢&#xff0c;在这里只针对刚学程序设计的学生&#xff0c;就是说刚接触C语言没多久&#xff0c;有一点功底的学生看看&#xff0c;简陋的代码&#xff0c;简陋的实现&#xff0…...

SpringBoot整合Retry详细教程

问题背景 在现代的分布式系统中&#xff0c;服务间的调用往往需要处理各种网络异常、超时等问题。重试机制是一种常见的解决策略&#xff0c;它允许应用程序在网络故障或临时性错误后自动重新尝试失败的操作。Spring Boot 提供了灵活的方式来集成重试机制&#xff0c;这可以通过…...

JS API事件监听(绑定)

事件监听 语法 元素对象.addEventListener(事件监听,要执行的函数) 事件监听三要素 事件源&#xff1a;那个dom元素被事件触发了&#xff0c;要获取dom元素 事件类型&#xff1a;用说明方式触发&#xff0c;比如鼠标单击click、鼠标经过mouseover等 事件调用的函数&#x…...

ceph手动部署

ceph手动部署 一、 节点规划 主机名IP地址角色ceph01.example.com172.18.0.10/24mon、mgr、osd、mds、rgwceph02.example.com172.18.0.20/24mon、mgr、osd、mds、rgwceph03.example.com172.18.0.30/24mon、mgr、osd、mds、rgw 操作系统版本&#xff1a; Rocky Linux release …...

superset load_examples加载失败解决方法

如果在执行load_examples命令后,出现上方图片情况,或是相似报错(url error\connection error),大概率原因是python程序请求github数据,无法访问. 因此我们可以将数据下载在本地来解决. 1.下载zip压缩文件,存放到本地 官方示例地址:GitHub - apache-superset/examples-data …...

wareshark分析mysql协议的数据包

使用wareshark 分析mysql协议的数据包&#xff0c;是每个dba都应该掌握的技能&#xff0c;掌握以后&#xff0c;就可以通过tcpdump抓包分析&#xff0c;得到连接报错的信息了。 tcpdump抓包命令&#xff1a; tcpdump -nn -i bond0 dst 10.21.6.72 and port 4002 -w 1129_tcpdu…...

HarmonyOS4+NEXT星河版入门与项目实战(25)------UIAbility启动模式(文档编辑案例)

文章目录 1、启动模式2、Specified启动模式实现步骤3、文档编辑案例1、文件创建2代码实现3、Statge 创建4、添加配置1、启动模式 Singleton启动模式: 每个 UIAbility 只存在一个实例,是默认的启动模式,任务列表中只会存在一个相同的 UIAbilityStandard启动模式: 每次启动 U…...

webpack 项目访问静态资源

使用 webpack dev serve 启动 react 项目后&#xff0c;发现无法使用 http://localhost:8080/1.png 访问到项目的 /static 目录下的 1.png 文件。我的 webpack-dev.js 配置如下&#xff1a; const webpack require(webpack) const webpackMerge require(webpack-merge) cons…...

‌UNION和UNION ALL区别

文章目录 结果集的处理方式‌&#xff1a;‌对重复记录的处理‌&#xff1a;‌排序处理‌&#xff1a;‌执行效率‌&#xff1a; ‌UNION和UNION ALL的主要区别在于结果集的处理方式、对重复记录的处理、排序处理以及执行效率。‌‌ 结果集的处理方式‌&#xff1a; ‌UNION‌…...

Rook入门:打造云原生Ceph存储的全面学习路径(下)

文章目录 六.Rook部署云原生CephFS文件系统6.1 部署cephfs storageclass6.2 创建容器所需cephfs文件系统6.3创建容器pod使用rook-cephfs提供pvc6.4 查看pod是否使用rook-cephfs 七.Ceph Dashboard界面7.1 启用dashboard开关7.2 ceph-dashboard配置外部访问7.3 Dashboard web ad…...

RabbitMQ消息可靠性保证机制6--可靠性分析

在使用消息中间件的过程中&#xff0c;难免会出现消息错误或者消息丢失等异常情况。这个时候就需要有一个良好的机制来跟踪记录消息的过程&#xff08;轨迹溯源&#xff09;&#xff0c;帮助我们排查问题。 在RabbitMQ中可以使用Firehose实现消息的跟踪&#xff0c;Firehose可…...

如何优化网站关键字/佛山企业用seo策略

目录 一、Pandas 二、Pandas-Serise 三、DataFrame 1、DataFrame的创建 2、 删除操作&#xff1a; 3、获取操作&#xff1a; 获取列数据&#xff1a; 获取行数据&#xff1a; 根据具体条件获取数据 4、文件操作 四、使用jupyter notebook完成文件相关操作 五、Seabor…...

中企动力网站建设精品案例/什么推广方式能快速引流

基于springboot实现协同过滤算法商品推荐系统演示开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Mav…...

成都微网站开发/策划推广活动方案

今天&#xff0c;刚好要写开源项目的后台数据了&#xff0c;所以学习了一下php的命名空间&#xff0c;收获多多&#xff0c;所以总结了一下这篇文章&#xff0c;好了&#xff0c;废话不多说&#xff0c;上代码吧我们该如何命名空间呢&#xff1f;<?php // 使用命名空间的关…...

互联网公司经营范围有哪些/培训seo哪家学校好

用于在页面做下拉框使用. 在Action 把一个ProjectNature对象放到了共享域. listKey 表示在选择后传参是使用 ProjectNature 对象的id字段传 listValue 表示我在页面下拉框展示 ProjectNature 的name字段的值 headrerKey headerValue 则为默认值 <s:select name"p…...

做外贸什么网站比较好/网络推广竞价外包

在虚拟机中安装 Ubuntu 步骤 安装前的准备和基本安装设置语言环境安装常用软件 1. 安装前的准备和基本安装 1.1 安装前的准备 访问 http://cn.ubuntu.com/download/ 下载 Ubuntu 16.04 版本在操作系统上安装 VMWare 虚拟机软件 为什么要使用虚拟机&#xff1f; 不需要准备…...

椒江区建设局网站/seo经理招聘

Gradle(二) —— 运行自己的应用1. 综述 完成简单的 Hello World 之后&#xff0c;怎么运行我们的程序呢&#xff0c;有三种方法&#xff1a; 直接用 IDE&#xff0c;右键 -> run Main.main在 gradle.build 中加入 application plugin&#xff0c;进行一些配置&#xff0c;然…...