写点什么

老司机狂飙之路 --EventBus 原理简要分析

作者:芝麻粒儿
  • 2022 年 7 月 12 日
  • 本文字数:3925 字

    阅读完需:约 13 分钟

老司机狂飙之路--EventBus原理简要分析

👉关于作者

众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣!!!专注于 Android/Unity 和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)


​👉即将学会

了解 EventBus 原理,知道 EventBus 的工作核心。

👉背景

🙈小空:小芝,你来一下。

🙎小芝(嘟嘴疑问):干嘛啊😝

🙈小空(😎):我们接着将 EventBus 的原理分析。

🙎小芝(😧):啊,可我不想挺啊。

🙈小空:开始了。

🙎小芝(😩):救命啊。

👉实践过程

正式开始之前,咱先说一说上一篇,说到的索引类,点进去查看,如是:

/** * https://juejin.cn/user/4265760844943479/posts* This class is generated by EventBus, do not edit. */ public class MyEventBusIndex implements SubscriberInfoIndex {     private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;     static {         SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();         putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {            new SubscriberMethodInfo("onMessageEvent", cn.akitaka.www.textcode.event.MessageEvent.class,                    ThreadMode.POSTING, 1, true),        }));    }     private static void putIndex(SubscriberInfo info) {         SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);    }      @Override    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {         SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);         if (info != null) {            return info;        } else {            return null;        }    }}
复制代码


一步一步分析:首先它是实现 SubscriberInfoIndex 接口 ,在 addIndex 方法中的参数,看看具体实现:

使用 Map 保存 SubscriberInfo,即所有订阅者的接受方法,然后 接口内定义的方法 getSubscriberInfo,通过 subscriberClass(key 订阅者)去获得 SubscriberInfo (value,也就是订阅方法集合);通俗的讲呢 就是以订阅者为单位,将订阅者类所有的订阅函数及相关参数,封装到 SimpleSubscriberInfo 中去了,方便 EventBus 在注册过程中使用;声明下 SimpleSubscriberInfo 是在编译时候生成的,这样运行的时候可直接用,省去了运行中反射操作的资源和时间消耗,高效运行!

走到这,突然多出个问题,他的生成过程呢?其实根据 build.gradle 的配置可以看出点端倪;

首先明确的是 MyEventBusIndex 是通过第三方的那个 android-apt 或者 Gradle 本身提供的 annotationProcessor,与 EventBus 提供的 EventBusAnnotationProcessor 合体,协同生产出的玩意, EventBus 控制着 EventBusAnnotationProcessor-详情看源码( package org.greenrobot.eventbus.annotationprocessor;):

实战篇

步入正题;先来个整体的玩意



订阅者和事件的发布所在线程不固定,取决于 ThreadMode,但是每个线程都有相关联的 Queue。

首先从注册开始看


EventBus.getDefault().register(this) 咱先看getDefault()里面有什么,如下/*** https://juejin.cn/user/4265760844943479/postsConvenience 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; }
复制代码


再深入看 new EventBus 做了啥?


/*** Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a central bus, consider {@link #getDefault()}.*/ public EventBus() {    this(DEFAULT_BUILDER);}
复制代码


传进去个变量?去瞅瞅 ,在这个过程中建造者模式蹦出来了,你有没有发现?

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); 

发现各种初始化在构造中使用 builder 实现,回头看咱们上篇文章创建索引的时候使用

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); 

还是用 buidler,异曲同工啊;继续代码点点点,深入深入,分析出流程: 



  1. 注册者和事件消费是 1:N 的关系,一个 Event 与注册者也是 1:N 的关系,即 register(this)对比当前的消费事件是 1:N,post()对比 register 也是 1:N

  2. 注册的不可再注册

说完注册,我们再说一说那事件分发,我们先从 post 入手;


/** Posts the given event to the event bus. */ public void post(Object event) {     //1.获取当前线程postingstate    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 {            while (!eventQueue.isEmpty()) {                postSingleEvent(eventQueue.remove(0), postingState);            }        } finally {            postingState.isPosting = false;            postingState.isMainThread = false;         }    }}
复制代码


那啥是 PostingThreadState,他包含了当前事件队列,以及订阅者订阅事件等信息,拿到这些就可以去实现分发啦;可我明明看见上面使用的 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));        }    }}
复制代码


发现又多了个 postSingleEventForEventType() ,再深入,发现是 postToSubscription(),而这里面又是 invokeSubscriber();至此结束我们捋一捋:



这次该轮到取消注册 unregister,这个代码少一些,直白一点如下:


/** Unregisters the given subscriber from all event classes. */ 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());    }} /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ 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--;            }        }    }}
复制代码


unregister 流程:

  1. 传进来需要取消的订阅者

  2. 遍历获得该订阅者的所有事件

  3. 使用 unsubscribeByEventType 去遍历事件,看看哪些订阅者都有这个事件,然后从集合删除订阅者,订阅者不再和事件有关联

恭喜小芝成为老司机阶段!下次小空带你动手自己实现一个 EventBus!

👉其他

📢作者:小空和小芝中的小空

📢转载说明-务必注明来源:https://www.infoq.cn/profile/DB2492B85795C4/publish

📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

发布于: 刚刚阅读数: 4
用户头像

芝麻粒儿

关注

尺有所短;寸有所长。 2020.08.29 加入

👑CSDN博客专家-华为云享专家-Android/Unity领域优质作者 🏅目前在模拟医学行业做Android/Unity双端开发 🏆微信公众号:【空名先生】 🏆QQ交流群:204918251或877807592

评论

发布
暂无评论
老司机狂飙之路--EventBus原理简要分析_android_芝麻粒儿_InfoQ写作社区