EventBus 3.0 源码深度解析:注册、事件分发与取消注册全流程拆解
教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载导读EventBus 是 Android 开发中最经典的事件总线框架之一它以发布-订阅模式将业务逻辑与视图层解耦让组件之间的通信不再依赖强引用。本文以 YCBlogs 仓库中 19.EventBus源码分析.md 为骨架从getDefault()单例获取、register()订阅注册、post()事件分发到unregister()取消注册逐行拆解核心源码并结合仓库中 ThreadLocal分析、反射机制基础介绍 等关联笔记讲透 EventBus 的四种线程模型、反射查找订阅方法、APT 编译期索引加速等底层原理。读完本文你将具备独立阅读事件总线类框架源码的能力并能回答面试中关于 EventBus 的高频问题。一、EventBus 使用前必知三要素与四种线程模型1.1 EventBus 的三要素EventBus 围绕三个核心角色工作这也是理解其源码的前提Event事件可以是任意类型的对象作为消息载体在发布者与订阅者之间传递。Subscriber事件订阅者接收事件并处理的对象。在 EventBus 3.0 之前消息处理方法只能命名为onEvent、onEventMainThread、onEventBackgroundThread、onEventAsync分别对应四种线程模型在 EventBus 3.0 之后处理方法可以随意命名但必须添加Subscribe注解并显式指定线程模型默认POSTING。Publisher事件发布者可以在任意线程的任意位置发送事件直接调用EventBus.getDefault().post(Object)即可。可以自行实例化 EventBus 对象但一般使用默认单例就够了——post会根据参数的类型自动调用订阅了相应类型事件的函数。1.2 EventBus 的四种 ThreadMode线程模型线程模型执行线程注意事项POSTING默认事件在哪个线程发布处理函数就在哪个线程运行发布与接收同线程处理函数中尽量避免耗时操作否则会阻塞事件传递甚至引发 ANRMAIN始终在 UI 线程执行处理时间不能太长否则会 ANRBACKGROUND事件在 UI 线程发布则在新线程执行事件本来就在子线程发布则直接在发布线程执行处理函数中禁止进行 UI 更新操作ASYNC无论事件在哪个线程发布都在新建的子线程中执行同样禁止进行 UI 更新操作1.3 EventBus 的基本使用// 1. 自定义一个事件类 public class MessageEvent { } // 2. 在需要订阅事件的地方注册事件 EventBus.getDefault().register(this); // 3. 发送事件 EventBus.getDefault().post(messageEvent); // 4. 处理事件3.0 之后方法可以随便取名 // 需要添加 Subscribe 注解并指定线程模型不指定则默认为 POSTING Subscribe(threadMode ThreadMode.MAIN) public void Hhhh(MessageEvent messageEvent) { // 处理事件 } // 5. 取消事件订阅 EventBus.getDefault().unregister(this);调用原理通俗版EventBus 内部存储了一堆以Subscribe注解标记的方法。post时根据传入参数的Class类型在内部 Map键就是参数的 Class 类型中扫描查找参数匹配的方法再用反射调用它。使用利弊分析EventBus 的优势是解耦——将业务和视图分离代码实现容易且 3.0 之后可以通过 APT 预编译生成索引找到订阅者避免运行期反射解析大大提升效率。但滥用会导致逻辑分散、维护困难大量使用 EventBus 也会让代码可读性变差。仓库笔记 APT技术详解 中明确指出EventBus3 与 Dagger2、ButterKnife 一样都是典型的编译期注解APT应用框架。二、EventBus 注册源码解析从 getDefault 到 subscribe2.1 EventBus.getDefault() 获取对象双重检查单例/** Convenience singleton for apps using a process-wide EventBus instance. */ public static EventBus getDefault() { if (defaultInstance null) { synchronized (EventBus.class) { if (defaultInstance null) { defaultInstance new EventBus(); } } } return defaultInstance; }这是典型的双重检查锁DCL单例外层if判断避免每次调用都进入同步块提高并发下的效率内层if配合synchronized防止多线程并发创建多个实例。2.2 构造器初始化EventBus 的核心数据结构private static final EventBusBuilder DEFAULT_BUILDER new EventBusBuilder(); 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 的两个核心存储结构subscriptionsByEventType以事件类型为 key以订阅者列表CopyOnWriteArrayList为 value用于事件投递时快速找到订阅者typesBySubscriber以订阅者为 key以订阅者订阅的所有事件类型为 value用于unregister()时快速找到该订阅者订阅了哪些事件。这两个 Map 一正一反配合完成注册与反注册。另外构造器还按线程模型预置了三种 PostermainThreadPoster、backgroundPoster、asyncPoster它们会在事件分发阶段发挥作用。2.3 register(this) 注册源码解析public void register(Object subscriber) { // 首先获取订阅者的类对象 Class? subscriberClass subscriber.getClass(); // 用 subscriberMethodFinder 提供的方法找到在 subscriber 这个类里面订阅的内容 ListSubscriberMethod subscriberMethods subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }register()的逻辑很清晰先拿到订阅者的字节码对象调用findSubscriberMethods()找出该订阅者所有的订阅方法信息然后在同步块中遍历逐个调用subscribe()完成注册。SubscriberMethod订阅方法类中主要保存订阅方法的Method对象、线程模式、事件类型、优先级、是否是粘性事件等属性。2.4 findSubscriberMethods缓存优先注解索引与反射兜底ListSubscriberMethod findSubscriberMethods(Class? subscriberClass) { // 首先从缓存中读取 ListSubscriberMethod subscriberMethods METHOD_CACHE.get(subscriberClass); if (subscriberMethods ! null) { return subscriberMethods; } // 是否忽略注解器生成的 MyEventBusIndex 类 if (ignoreGeneratedIndex) { // 利用反射来获取订阅类中的订阅方法信息 subscriberMethods findUsingReflection(subscriberClass); } else { // 从注解器生成的 MyEventBusIndex 类中获得订阅类的订阅方法信息 subscriberMethods findUsingInfo(subscriberClass); } // 如果订阅者中不存在 Subscribe 注解且为 public 的订阅方法则抛出异常 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; } } // METHOD_CACHE是一个 map 集合键是 class 类型 MapClass?, ListSubscriberMethod METHOD_CACHE new ConcurrentHashMap();关键点缓存优先METHOD_CACHE是ConcurrentHashMap同一个订阅类只会解析一次避免重复反射开销双路径查找ignoreGeneratedIndex为false默认值时走findUsingInfo优先使用 APT 生成的MyEventBusIndex索引类为true时走findUsingReflection纯反射查找异常兜底如果订阅类中既没有Subscribe注解的 public 方法直接抛出EventBusException。2.5 findUsingInfo索引与反射的组合查找private ListSubscriberMethod findUsingInfo(Class? subscriberClass) { FindState findState prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz ! null) { // 获取订阅者信息没有配置 MyEventBusIndex 返回 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); }这段代码揭示了 EventBus 3.0 性能优化的核心思路通过getSubscriberInfo()获取订阅者信息。如果通过EventBusBuilder配置了 APT 生成的MyEventBusIndex就能直接拿到SubscriberMethod[]数组无需反射遍历方法如果没有配置索引则退回findUsingReflectionInSingleClass用反射查找moveToSuperclass()表示查找会沿着继承链向上遍历父类中的订阅方法也会被收集getMethodsAndRelease(findState)负责回收FindState对象对象池复用并返回订阅方法 List。补充反射是什么仓库笔记 反射机制基础介绍 指出反射是在运行状态中对于任意一个类都能知道它的所有属性和方法并能动态调用任意方法。EventBus 正是单纯的反射机制应用框架的典型代表而 3.0 引入的 APT 索引MyEventBusIndex则把一部分运行期反射工作前移到了编译期属于注解 反射的结合优化。2.6 subscribe订阅方法的真正注册回到register(this)遍历订阅方法后逐个调用subscribe()private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { // 获取订阅方法的参数类型即事件类型 Class? eventType subscriberMethod.eventType; // 根据订阅者和订阅方法构造一个订阅事件 Subscription newSubscription new Subscription(subscriber, subscriberMethod); // 获取当前事件类型对应的 Subscription 的 List 集合 CopyOnWriteArrayListSubscription subscriptions subscriptionsByEventType.get(eventType); // 该事件对应的 List 集合不存在则重新创建并保存在 subscriptionsByEventType 中 if (subscriptions null) { subscriptions new CopyOnWriteArrayList(); subscriptionsByEventType.put(eventType, subscriptions); } else { // 订阅者已经注册则抛出 EventBusException 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; } } // 通过订阅者获取该订阅者所订阅事件的集合 ListClass? subscribedEvents typesBySubscriber.get(subscriber); if (subscribedEvents null) { subscribedEvents new ArrayList(); typesBySubscriber.put(subscriber, subscribedEvents); } // 将当前的订阅事件类型添加到 subscribedEvents 中 subscribedEvents.add(eventType); // 粘性事件处理订阅粘性事件的订阅者会立刻收到之前发送过的粘性事件 if (subscriberMethod.sticky) { if (eventInheritance) { SetMap.EntryClass?, Object entries stickyEvents.entrySet(); for (Map.EntryClass?, 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存入subscriptionsByEventType供投递时按事件类型查订阅者同时将事件类型加入typesBySubscriber供反注册时按订阅者查事件类型粘性事件立即投递如果订阅方法是 sticky粘性的会立刻从stickyEvents缓存中取出历史粘性事件通过checkPostStickyEventToSubscription立即回调给订阅者。另外注意两点细节同一订阅者对同一事件重复注册会抛出EventBusException防止重复订阅订阅者列表采用CopyOnWriteArrayList写时复制保证并发遍历订阅者时的线程安全订阅者按priority优先级降序插入高优先级订阅者先收到事件且可在postSingleEventForEventType中通过cancel取消后续订阅者的接收。三、EventBus 事件分发解析post 的完整链路3.1 从 post 方法入手/** Posts the given event to the event bus. */ public void post(Object event) { // 获取当前线程的 postingState PostingThreadState postingState currentPostingThreadState.get(); // 取得当前线程的事件队列 ListObject eventQueue postingState.eventQueue; // 将该事件添加到当前的事件队列中等待分发 eventQueue.add(event); if (!postingState.isPosting) { // 判断是否是在主线程 post postingState.isMainThread Looper.getMainLooper() Looper.myLooper(); 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; } } }post()的流程先从PostingThreadState对象取出事件队列把当前事件插入队列随后在isPosting为 false 时置位并循环取出队列中的事件交给postSingleEvent分发直到队列清空。isPosting标记保证了同一个线程内的事件分发是串行、可重入的。3.2 什么是 PostingThreadStatefinal static class PostingThreadState { final ListObject eventQueue new ArrayListObject(); // 当前线程的事件队列 boolean isPosting; // 是否有事件正在分发 boolean isMainThread; // post 的线程是否是主线程 Subscription subscription; // 订阅者 Object event; // 订阅事件 boolean canceled; // 是否取消 }PostingThreadState保存了当前线程分发事件所需的所有状态事件队列、是否正在分发、post 线程是否为主线程、当前正在处理的订阅者与事件、以及取消标记。3.3 PostingThreadState 怎么获得ThreadLocal 按线程隔离private final ThreadLocalPostingThreadState currentPostingThreadState new ThreadLocalPostingThreadState() { Override protected PostingThreadState initialValue() { return new PostingThreadState(); } };这里使用了ThreadLocal。仓库笔记 ThreadLocal分析 明确指出ThreadLocal 是一个线程内部的数据存储类通过它可以在指定线程中存储数据且这段数据不会与其他线程共享。每个线程持有一个属于自己的变量副本通过get、set访问修改。EventBus 正是利用 ThreadLocal 的这个特性为每个线程保存独立的PostingThreadState从而保证各线程的事件队列互不干扰同一线程内多个post调用共享同一个队列形成串行分发多线程同时post不同事件时互不冲突。3.4 postSingleEvent事件继承链的广播private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { // 得到事件类型 Class? eventClass event.getClass(); boolean subscriptionFound false; // 是否触发订阅了该事件(eventClass)的父类,以及接口的类的响应方法 if (eventInheritance) { ListClass? 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) { Log.d(TAG, No subscribers registered for event eventClass); } if (sendNoSubscriberEvent eventClass ! NoSubscriberEvent.class eventClass ! SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } } }两个要点事件继承广播eventInheritance默认值为true可通过EventBusBuilder配置。为 true 时通过lookupAllEventTypes找出事件的所有父类和实现的接口逐一尝试分发——即订阅了父类事件的订阅者也能收到子类事件无订阅者兜底如果没有任何订阅者接收可打印日志logNoSubscriberMessages也可发送NoSubscriberEvent让开发者感知sendNoSubscriberEvent。3.5 postSingleEventForEventType按事件类型取订阅者private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class? eventClass) { CopyOnWriteArrayListSubscription subscriptions; synchronized (this) { // 根据事件类型获取所有的订阅者 subscriptions subscriptionsByEventType.get(eventClass); } // 向每个订阅者分发事件 if (subscriptions ! null !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { postingState.event event; postingState.subscription subscription; boolean aborted false; 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; }该方法从subscriptionsByEventType中取出该事件类型对应的订阅者集合CopyOnWriteArrayList遍历并调用postToSubscription分发。注意postingState.canceled机制高优先级订阅者可以在处理方法中调用cancelEventDelivery()终止后续订阅者的接收与subscribe()中的优先级插入逻辑呼应。3.6 postToSubscription四种线程模型的分流执行private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case POSTING: // 默认的 ThreadMode表示在执行 Post 操作的线程直接调用订阅者的事件响应方法 // 不论该线程是否为主线程UI 线程 invokeSubscriber(subscription, event); break; case MAIN: // 在主线程中执行响应方法 if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(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); } }这是事件分发的最终落点也是四种 ThreadMode 真正生效的地方ThreadMode主线程 post 时子线程 post 时POSTING直接反射调用直接反射调用MAIN直接反射调用交给mainThreadPoster排队切回主线程BACKGROUND交给backgroundPoster排队到后台线程直接反射调用ASYNC交给asyncPoster排队到线程池交给asyncPoster排队到线程池最终都通过invokeSubscriber(subscription, event)以反射方式调用订阅者方法对应仓库笔记 反射机制基础介绍 中反射可以动态调用任意一个对象的方法的能力。3.7 事件分发过程总结获取当前线程的PostingThreadState对象从而拿到当前线程的事件队列ThreadLocal 隔离将事件加入队列并串行取出通过事件类型含父类/接口查找到所有订阅者集合根据订阅方法的 ThreadMode 决定执行线程最终通过反射执行订阅者中的订阅方法。四、EventBus 取消注册解析unregister 全流程4.1 unregister(this) 方法入手/** Unregisters the given subscriber from all event classes. */ public synchronized void unregister(Object subscriber) { // 获取订阅者的所有订阅的事件类型 ListClass? subscribedTypes typesBySubscriber.get(subscriber); if (subscribedTypes ! null) { for (Class? eventType : subscribedTypes) { // 从事件类型的订阅者集合中移除订阅者 unsubscribeByEventType(subscriber, eventType); } typesBySubscriber.remove(subscriber); } else { Log.w(TAG, Subscriber to unregister was not registered before: subscriber.getClass()); } }unregister()直接利用了注册时维护的typesBySubscriber反向索引先取出该订阅者订阅的所有事件类型逐个调用unsubscribeByEventType从对应事件类型的订阅者集合中移除最后把该订阅者从typesBySubscriber中移除。若订阅者从未注册过则打印警告日志。4.2 unsubscribeByEventType从订阅者集合中移除private void unsubscribeByEventType(Object subscriber, Class? eventType) { // 获取事件类型的所有订阅者 ListSubscription 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--; } } } }移除时先把对应Subscription的active标记置为 false防止移除后仍被异步 Poster 回调再将其从CopyOnWriteArrayList中删除。遍历时通过i--、size--处理连续多个订阅同类型事件的场景。4.3 取消注册过程总结首先获取订阅者的所有订阅事件类型来自typesBySubscriber遍历订阅事件类型根据每个事件类型获取所有订阅了该事件的订阅者集合来自subscriptionsByEventType将该订阅者从对应集合中移除并置active false最后将订阅者从typesBySubscriber集合中移除。五、EventBus 工作原理总结5.1 订阅逻辑用register()方法注册一个订阅者获取该订阅者的所有订阅方法缓存优先其次 APT 索引最后反射根据该订阅者订阅的所有事件类型将订阅者存入以事件类型为 key、所有订阅者为 value的 map 集合subscriptionsByEventType将订阅事件类型添加到以订阅者为 key、订阅者所有订阅事件为 value的 map 集合typesBySubscriber如果订阅的是粘性事件从粘性事件缓存区取出之前发送过的粘性事件立即响应。5.2 事件发送逻辑首先获取当前线程的事件队列ThreadLocal 存储的PostingThreadState将要发送的事件添加到事件队列中根据发送的事件类型获取所有订阅者支持父类/接口继承广播根据响应方法的 ThreadMode在相应线程通过反射执行订阅者的订阅方法。5.3 取消逻辑通过unregister()拿到要取消的订阅者得到该订阅者的所有订阅事件类型遍历事件类型根据每个事件类型获取所有订阅者集合并从集合中删除该订阅者将订阅者从typesBySubscriber集合中移除。六、延伸思考EventBus 的模式本质与 3.0 性能优化6.1 EventBus 是观察者模式还是中介者模式仓库笔记 中介者设计模式 对这个问题做过专门讨论EventBus 常被看作观察者模式的实现框架因为它需要定义 Observer订阅者通过register()注册通过post()给订阅者发消息。但 EventBus 作为事件处理的中心事件的派送、订阅都通过这个中心完成架构上又带有中介者模式的色彩。结论是EventBus 本质是观察者模式的实现同时借助了中介者思想来集中管理事件的分发并且它没有中介模式大而臃肿的上帝类问题——订阅者与订阅者之间完全解耦只与总线交互。6.2 3.0 的 APT 编译期索引优化EventBus 2.x 完全依赖运行期反射查找订阅方法每次注册都要遍历类的方法性能有损耗。3.0 之后引入注解处理器APT可以在编译期扫描Subscribe注解并生成MyEventBusIndex索引类。运行时若通过EventBusBuilder配置了该索引findUsingInfo()就能直接按索引拿到订阅方法数组跳过反射遍历这正是findSubscriberMethods中ignoreGeneratedIndex分支的由来。仓库笔记 APT技术详解 也印证了这一机制APT 就是在代码编译期间对注解进行处理并生成 Java 文件EventBus3、Dagger2、ButterKnife 都是这一技术的代表应用。6.3 常见面试追问为什么post()用的是 ThreadLocal 而不是普通成员变量因为事件可以在任意线程发送每个线程需要独立的队列与分发状态ThreadLocal 保证线程间隔离、互不阻塞详见 ThreadLocal分析。EventBus 为什么用CopyOnWriteArrayList存订阅者事件分发时可能并发遍历订阅者而注册/反注册操作较少CopyOnWriteArrayList 的写时复制策略能在并发读时提供快照安全避免ConcurrentModificationException。粘性事件什么时候触发在subscribe()时立即补发历史粘性事件stickyEvents缓存与postSticky()配合使用。事件继承广播如何关闭通过EventBusBuilder.eventInheritance(false)关闭关闭后只有精确匹配事件类型的订阅者才会收到事件。相关阅读仓库内延伸笔记EventBus 与 LiveData 事件总线的对比实践本文分析了 EventBus 使用原理、RxBus 原理以及为何选用具备生命周期感知能力的 LiveData 来替代 EventBus/RxBus可避免忘记反注册导致的内存泄漏。ThreadLocal 深入分析理解currentPostingThreadState的线程隔离基础。反射机制基础介绍理解invokeSubscriber动态调用的底层原理。APT 技术详解理解MyEventBusIndex编译期索引的生成机制。中介者设计模式理解 EventBus 在模式层面的定位。Java 反射问题汇总EventBus 作为反射应用框架的常见问题归纳。说明本文所有源码均取自 EventBus 3.x 公开源码与仓库笔记 19.EventBus源码分析.md 中摘录的版本保持一致仓库内相关笔记路径如上所列可在本仓库中继续深入阅读。赞分享教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载相关推荐终极指南EventBus从注册到事件分发的完整流程详解终极指南EventBus从注册到事件分发的完整流程详解 EventBus是一个专为Android和Java设计的事件总线框架它能够简化组件间的通信减少代码消息路由异步编程Vue.js 源码分析组件注册机制全局注册与局部注册深度剖析Vue.js 源码分析组件注册机制全局注册与局部注册深度剖析 导读 本文基于 vue analysis 仓库对 Vue.js 2.x 源码进行剖析聚焦「文档教程前端如何快速掌握ALEAPP安卓日志与Protobuf解析神器完全指南如何快速掌握ALEAPP安卓日志与Protobuf解析神器完全指南 在数字取证和安卓数据分析领域ALEAPPAndroid Logs Events And后端消息路由上一篇Backrest安全最佳实践身份验证、数据加密和访问控制完整指南下一篇Factorio Learning Environment为LLM打造的开源学习框架创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考