源码解读系列(二)EventBus3.1.1

1、EventBus的构造过程

使用EventBus的时候,首先要获取EventBus
eventbus
EventBus
构造方法是一个双重检查的单例模式。
EventBus
调用了 EventBus(DEFAULT_BUILDER);传入了一个默认的EventBusBuilder
EventBus
可以看到使用了EventBusBuilder来对EventBus进行了各种参数的配置

2、EventBus的订阅过程

EventBus
EventBus
subscriberMethodFinder.findSubscriberMethods(subscriberClass);方法找出一个SubscriberMethod的集合,这个集合就是所有传进来的订阅方法,接着遍历所有订阅者的订阅方法使用 subscribe(subscriber, subscriberMethod);来进行注册。
很明显注册方法完成了两件事:

1、查找所有订阅者的订阅方法
2、完成所有订阅者的注册

我们看看SubscriberMethod类
EventBus
原来这个类是用来保存订阅者的Method对象,线程模式,事件类,优先级,是否是黏性事件等基本参数。
至于methodString字符串是使用method对象和eventType对象构造的一个字符串,用于public boolean equals(Object other)方法,可以更加严谨正确的判断是否是同一个对象。

如何查找所有订阅者的订阅方法的

subscriberMethodFinder.findSubscriberMethods(subscriberClass);
EventBus
首先从缓存里获取所有的订阅者方法信息,如果获取到就直接返回,没有获取到就构造。
如果我们使用了EventIndex,也就是EventBus开启了索引模式,那么ignoreGeneratedIndex就为false,如果没有开启索引模式ignoreGeneratedIndex则为true。
最后把获取的订阅者信息加入到缓存里。

我们看看在开启了索引的情况下findUsingInfo(subscriberClass);这个方法做了什么

EventBus
1、 findState.subscriberInfo = getSubscriberInfo(findState);获取订阅者信息
如果获取到了订阅者信息subscriberInfo,就可以通过findState.subscriberInfo.getSubscriberMethods();来获取订阅者的方法信息,然后判断是否已经添加过该订阅者的方法信息,如果没有添加过则加入到订阅者方法信息的一个List中
2、如果没有获取到订阅者信息subscriberInfo则调用findUsingReflectionInSingleClass(findState);通过名字可以看出这是一个使用了放射来获取订阅者方法信息的方法。
3、最后通过getMethodsAndRelease(findState);方法来对FindState做处理,并返回所有订阅者方法的List集合。
4、那么什么情况下可以获取到订阅者信息subscriberInfo呢?
我们要看一下 findState.subscriberInfo = getSubscriberInfo(findState);方法

EventBus如果findState.subscriberInfo不是空并且父类的subscriberInfo也不是空,那么就获取订阅者信息并返回。
如果有一个是空,就判断是否存在索引,如果存在则从索引里获取订阅者信息并返回,如果不存在索引则返回null,然后外层函数会调用findUsingReflectionInSingleClass
EventBus
因为在我们调用getSubscriberInfo方法之前执行了findState.initForSubscriber(subscriberClass);方法,在这个方法里会把subscriberInfo置空,所以这个 **if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null)**正常情况下都是false,判断貌似是多余的,有知道这里代码的深意的大神请留言啊。
EventBus

如果没有开启索引则会调用findUsingReflection(subscriberClass);方法

EventBus
仍然是先构建了FindState,然后调用了 **findUsingReflectionInSingleClass(findState);**方法
注意这个方法就是在开启了索引但是没有找到订阅者信息也会调用的方法,所以会有两个地方调用此方法哦。
EvenBus

 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
            methods = findState.clazz.getMethods();
            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()));
                        }
                    }
                }
                ...
            }
        }
    }

methods = findState.clazz.getDeclaredMethods();通过反射来获取订阅者中所有的方法,并根据方法类型。参数和注解来找到订阅方法,最后把所有的订阅方法通过 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));方法保存到findState对象中。

如何注册订阅者的

在查找完所有的订阅者的订阅方法后就要对所有的订阅方法进行注册。

 for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
  	}
    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//--------核心方法1---------//
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//--------核心方法2---------//
        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);//--------核心方法3---------//
                break;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//--------核心方法4---------//
        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);

根据订阅者和订阅方法创建一个订阅对象

CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);

根据eventType事件类型获取订阅者对象集合,如果集合为Null则新建一个空集合,并将订阅者集合根据事件类型保存在一个MAP中 subscriptionsByEventType.put(eventType, subscriptions);

subscriptions.add(i, newSubscription);

按照订阅方法的优先级插入到订阅对象集合中,完成订阅方法的注册。哈哈看到了吧,我们的订阅方法的优先级属性是在这里进行应用的哦O(∩_∩)O~~

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

通过订阅者subscriber获取事件类型集合subscribedEvents,如果subscribedEvents为nul则重新创建subscribedEvents,将新建的事件类型集合subscribedEvents存储在MAP对象中,KEY值则是订阅对象,最后将事件类型eventType加入到subscribedEvents中。

如果是黏性事件,则从黏性事件保存队列stickyEvents中取出该事件类型的事件发送给当前订阅者。这里就是处理黏性事件的关键地方!
因为黏性事件是在订阅者注册以后才会发送的哦。正是因为这样,我们在会在注册以后仍然可以接受到事件。
黏性事件
综上:注册方法主要做了两件事
1、将订阅对象集合subscriptions根据事件类型eventType(KEY值)添加到subscriptionsByEventType(MAP)中
将事件类型集合subscribedEvents根据订阅者subscriber(KEY值)添加到typesBySubscriber(MAP)中
2、注册以后发送黏性事件

这样就完成了订阅者的注册过程

如何发送事件的

EventBus
EventBus
PostingThreadState postingState = currentPostingThreadState.get();
从PostingThreadState对象中取出事件队列,然后将当前的事件插入到事件队列eventQueue中,最后将队列中的事件交给postSingleEvent方法处理,并移除该事件。
EventBus
eventInheritance表示是否继续向上查找事件的父类,默认是true,可以通过EventBusBuilder设置。
当eventInheritance为true的时候,则通过lookupAllEventTypes(eventClass);找到所有的父类事件并存在List中,最后通过postSingleEventForEventType方法发送事件。
EventBus

subscriptions = subscriptionsByEventType.get(eventClass);

根据事件类(KEY)从注册时候的MAP中取出对应的订阅对象集合

postToSubscription(subscription, event, postingState.isMainThread);

遍历订阅对象集合,将事件event和对应的订阅对象传递给postToSubscription方法
EventBus
取出订阅事件的线程模型threadMode,然后根据不同的线程模型执行不同的方法。
1、如果是threadMode主线程并且发送事件的线程也是主线程则直接通过反射运行订阅的方法
2、如果threadMode不是主线程则使用mainThreadPoster将订阅事件添加到主线程队列中,mainThreadPoster其实是一个handler,通过handler将订阅方法切换到主线程执行。另外backgroundPoster和asyncPoster其实是一个Runnable
3、如果threadMode是POSTING,则不切换线程,发送事件的方法在什么线程就发送到什么线程,在使用这个threadMode的时候应当避免在主线程发送,以免造成主线程阻塞。

如何取消注册的

EventBus
EventBus

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

从MAP中根据订阅者KEY找到事件类型集合

unsubscribeByEventType(subscriber, eventType);

遍历事件类型集合并调用unsubscribeByEventType

typesBySubscriber.remove(subscriber);

把订阅者对应的事件类型从事件类型集合的MAP中移除
我们再看看unsubscribeByEventType(Object subscriber, Class<?> eventType) 方法
EventBus

subscriptionsByEventType.get(eventType);

从MAP中根据事件类型KEY找到订阅者对象集合

subscriptions.remove(i);

遍历订阅者对象集合,判断注册的时候传进来的订阅对象和现在要注销的订阅对象unregister(Object subscriber)是同一个对象就从订阅者对象集合中移除该订阅者。

在这里介绍一个小知识:MAP里保存了list,当你获取到该list对象的引用的时候,对其进行增删操作会同步到MAP里,而不用再手动put一次。
实验代码:

               List<String> strs = new ArrayList<>();
               for (int i =0;i<5;i++){
                   strs.add("i = "+i);
               }
               
                Map<String, List<String>> map = new HashMap<>();
                map.put("str",strs);//保存list
                
                List<String> strs2 = map.get("str");//取出保存的list
                for (String str :
                        strs2) {
                    Log.i("LHD", "处理前的map里的strs = " + str);
                }
                strs2.remove(0);//移除一个0,还剩1234
                strs2.remove(1);//移除一个2,还剩134

                List<String> strs3 = map.get("str");//获取MAP里的list
                for (String str :
                        strs3) {
                    Log.i("LHD", "处理后的map里的strs = " + str);
                }

打印的值:
MAP里保存List
以上就是EventBus3.0的源码解析啦O(∩_∩)O~~

补充:

一句话总结EventBus的订阅流程:在register注册的时候,获取到当前activity下的所有订阅方法,并根据订阅方法的要处理的事件类,将订阅方法加入到对应的list中。

首先在注册的时候,EventBus会获取到当前activity的所有的订阅方法,然后把这些订阅方法根据事件类型加入到一个MAP中,这个MAP的KEY是订阅类型,VALUE是一个list,而这个list就是所有接受对应Event事件的方法集合。

比如我们在activity中注册
Eventbus注册流程
EventBus会把当前activity下所有的订阅方法都注册到一个MAP里。
KEY 是 eventType 也就是TestEvent.class
VALUE 是CopyOnWriteArrayList,也就是所有要消费TestEvent事件的订阅方法的集合。
Subscription记录了订阅方法所在的activity和对应的SubscriberMethod,而SubscriberMethod则是将订阅方法method和eventType,线程模型,优先级,时候是粘性等属性封装成的一个包装类。
它们的关系如下:

new Subscription(subscriber, subscriberMethod)
new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky())

也就是将getTestEvent(TestEvent testEvent)这个方法和他的注解参数一起封装成了一个SubscriberMethod对象。然后再和MainAcitivity.class一起封装成了一个Subscription对象。
这样当EventBus发送事件的时候就会根据eventType也就是TestEvent.class为key,拿出所有TestEvent对应的订阅方法SubscriberMethod,然后根据优先级线程模型和是否是粘性等条件通知所有TestEvent的订阅方法。

//订阅方法伪代码:

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//1、获取当前订阅方法的eventType,其实就是订阅方法的参数的类
        Class<?> eventType = subscriberMethod.eventType;//TestEvent.class
//2、构建一个新的Subscription 对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//MainActivity.class,getTestEvent
 	//3、根据TestEvent拿出对应的list
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        int size = subscriptions.size();
//4、根据订阅方法的优先级属性进行排序,把订阅方法加入到list中去
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);//然后排序list,并把新的Subscription放进去
                break;
            }
        }

//保存所有的事件类型
        subscribedEvents.add(eventType);
//发送粘性事件
       Object stickyEvent = stickyEvents.get(eventType);
      checkPostStickyEventToSubscription(newSubscription, stickyEvent);
    }
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值