【Android】EventBus的使用及源码分析
文章目录
- 介绍
- 优点
- 基本用法
- 线程模式
- POSTING
- MAIN
- MAIN_ORDERED
- BACKGROUND
- ASYNC
- 黏性事件
- 源码
- 注册
- getDefault()
- register
- findSubscriberMethods
- 小结
- post
- postSticky
- unregister
介绍

优点
- 简化组件之间的通信
- 解耦事件发送者和接收者
- 在 Activity、Fragment 和后台线程中表现良好
- 避免复杂且容易出错的依赖关系和生命周期问题
- 让你的代码更简单
- 很快,很小
- 具有高级功能,如交付线程、订阅者优先级等。
基本用法
导入依赖
implementation "org.greenrobot:eventbus:3.3.1"
- 定义事件:
public static class MessageEvent { /* Additional fields if needed */ }
-
准备订阅者:声明并注释您的订阅方法,可以选择指定线程模式
@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);} -
发布活动:
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
- 特点:
- 在主线程中执行。
- 按顺序执行:事件会一个接一个地处理,不会乱序。
- 适用场景:依赖特定执行顺序的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;}
}
小结
- 根据单例设计模式创建一个
EventBus对象,同时创建一个EventBus.Builder对象对EventBus进行初始化,其中有三个比较重要的集合和一个SubscriberMethodFinder对象。 - 调用
register方法,首先通过反射获取到订阅者的Class对象。 - 通过
SubscriberMethodFinder对象获取订阅者中所有订阅的事件集合,它先从缓存中获取,如果缓存中有,直接返回;如果缓存中没有,通过反射的方式去遍历订阅者内部被注解的方法,将这些方法放入到集合中进行返回。 - 遍历第三步获取的集合,将订阅者和事件进行绑定。
- 在绑定之后会判断绑定的事件是否是粘性事件,如果是粘性事件,直接调用
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);}}...
}
- 获取当前线程的事件集合,将要发送的事件加入到集合中。
- 通过循环,只要事件集合中还有事件,就一直发送。
- 获取事件的
Class对象,找到当前的event的所有父类和实现的接口的class集合。遍历这个集合,调用发送单个事件的方法进行发送。 - 根据事件获取所有订阅它的订阅者集合,遍历集合,将事件发送给订阅者。
- 发送给订阅者时,根据订阅方法的线程模式调用订阅方法,如果需要线程切换,则切换线程进行调用;否则,直接调用。
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);}
}
- 将粘性事件加入到
EventBus对象的粘性事件集合中,当有新的订阅者进入后,如果该订阅者订阅了该粘性事件,可以直接发送给订阅者。- 将粘性事件发送给已有的事件订阅者。
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、移除订阅者中所有的订阅方法。
参考:
- greenrobot/EventBus(github.com)
- EventBus源码解析 - 掘金 (juejin.cn)
相关文章:
【Android】EventBus的使用及源码分析
文章目录 介绍优点基本用法线程模式POSTINGMAINMAIN_ORDEREDBACKGROUNDASYNC 黏性事件 源码注册getDefault()registerfindSubscriberMethods小结 postpostStickyunregister 介绍 优点 简化组件之间的通信 解耦事件发送者和接收者在 Activity、Fragment 和后台线程中表现良好避…...
【大数据学习 | Spark调优篇】Spark之内存调优
1. 内存的花费 1)每个Java对象,都有一个对象头,会占用16个字节,主要是包括了一些对象的元信息,比如指向它的类的指针。如果一个对象本身很小,比如就包括了一个int类型的field,那么它的对象头实…...
Linux:文件系统inode
早期,存储文件的设备是磁盘(当下的市场几乎都是SSD),但大家习惯的把它们都称为磁盘,磁盘是用来表示区分内存的存储设备。而在操作系统看来,这个存储设备的结构就是一个线性结构,这一点很重要。 …...
力扣难题解析
滑动窗口问题 76.最小覆盖子串 题目链接:76. 最小覆盖子串 - 力扣(LeetCode) 题目描述: 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空…...
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)
三、算法 算法这个词,我们都不陌生。 从接触计算机,就知道有“算法”这样一个神秘的名词存在。象征着专业、权威、神秘、高难等等。 算法是一组有序的解决问题的规则和指令,用于解决特定问题的一系列步骤。算法可以被看作是解决问题的方法…...
深度学习笔记——生成对抗网络GAN
本文详细介绍早期生成式AI的代表性模型:生成对抗网络GAN。 文章目录 一、基本结构生成器判别器 二、损失函数判别器生成器交替优化目标函数 三、GAN 的训练过程训练流程概述训练流程步骤1. 初始化参数和超参数2. 定义损失函数3. 训练过程的迭代判别器训练步骤生成器…...
网络安全开源组件
本文只是针对开源项目进行收集,如果后期在工作中碰到其他开源项目将进行更新。欢迎大家在评论区留言,您在工作碰到的开源项目。 祝您工作顺利,鹏程万里! 一、FW(防火墙) 1.1 pfSense pfSense项目是一个免费…...
Python毕业设计选题:基于django+vue的智慧社区可视化平台的设计与实现+spider
开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 养老机构管理 业主管理 社区安防管理 社区设施管理 车位…...
Oracle LinuxR7安装Oracle 12.2 RAC集群实施(DNS解析)
oracleLinuxR7-U6系统Oracle 12.2 RAC集群实施(DNS服务器) 环境 RAC1RAC2DNS服务器操作系统Oracle LinuxR7Oracle LinuxR7windows server 2008R2IP地址172.30.21.101172.30.21.102172.30.21.112主机名称hefei1hefei2hefei数据库名hefeidbhefeidb实例名…...
M2芯片安装es的步骤
背景:因为最近经常用到es,但是测试环境没有es,自己本地也没安装,为了方便测试,然后安装一下,但是刚开始安装就报错,记录一下,安装的版本为8.16.1 第一步:去官网下载maco…...
macos下brew安装redis
首先确保已安装brew,接下来搜索资源,在终端输入如下命令: brew search redis 演示如下: 如上看到有redis资源,下面进行安装,执行下面的命令: brew install redis 演示效果如下: …...
第六届金盾信安杯-SSRF
操作内容: 进入环境 可以查询网站信息 查询环境url https://114.55.67.167:52263/flag.php 返回 flag 就在这 https://114.55.67.167:52263/flag.php 把这个转换成短连接,然后再提交 得出 flag...
【论文投稿】国产游戏技术:迈向全球引领者的征途
【IEEE出版南方科技大学】第十一届电气工程与自动化国际会议(IFEEA 2024)_艾思科蓝_学术一站式服务平台 更多学术会议论文投稿请看:https://ais.cn/u/nuyAF3 目录 国产游戏技术能否引领全球? 一、国产游戏技术的崛起之路 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)
大家好,我叫小帅今天我们来继续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语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发,怎么说呢,在这里只针对刚学程序设计的学生,就是说刚接触C语言没多久,有一点功底的学生看看,简陋的代码,简陋的实现࿰…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
