事件注册

在EventBus3.0框架中订阅者对事件进行注册/订阅是通过EventBus类中的register方法来实现的,register的方法参数就是我们的订阅者的实例;

    public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}

findSubscriberMethods 

  在register方法中首先获取订阅者的实例的类型,然后通过subscriberMethodFinder类中的findSubscriberMethods方法来查找这个订阅者类型中的所有的消息事件的处理方法列表;获取到所有的消息处理方法后遍历进行订阅/注册;在subscribe方法的内部就是对EventBus类中的Map表进行赋值;

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注解器生成的MyEventBusIndex类
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
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;
}
}

  subscriberMethodFinder类中的findSubscriberMethods方法中首先是通过从缓存中获取相应的消息事件处理列表;由于EventBus框架采用的是反射机制来处理,性能上肯定没有直接去new对象的效率高,为了解决了这个造成的性能问题,EventBus框架采用编译期间进行对标注了“@Subscribe”注解的函数进行缓存;

在3.0版本中,EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析,处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快;对于缓存这块以后单独一个章节会讲到,这里不在做深入的分析;

    private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}

在findUsingReflection 方法中查找SubscriberMethod集合时,使用了FindState来传递消息; 在subscriberMethodFinder类中维护着一个数组长度默认值为4的静态数组,这个数组存储的就是FindState,这边保证了FindState类可以复用,避免重复创造过的FindState对象;

   static class FindState {
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128); Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo; void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
}

在FindState类中维护着2个类型字段;subscriberClass类型是订阅者的类型,clazz类型在初始化的时候和subscriberClass类型是一样,当skipSuperClasses为false时,循环查找基类时,clazz类型会重新赋值为订阅者基类的类型;

当FindState类初始化构造完成后,EventBus3.0框架就开始循环查找订阅者以及基类中的监听事件处理函数;关于查找的具体实现就在findUsingReflectionInSingleClass方法中;

    private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
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()));
}
}
} 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");
}
}
}

在方法的内部,首先通过反射中的getDeclaredMethods方法获取订阅者(或者是基类)中的所有方法;进而循环去校验方法是否是监听事件的处理函数;

从(modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0 可以看出,事件监听处理函数必须是public;事件监听处理函数中参数必须只有一个,这个从parameterTypes.length == 1就可以看出;最重要的是事件监听处理函数必须是“Subscribe”注解标注的;

当上述条件都满足的情况,EventBus3.0框架就认为这个函数就是事件监听处理函数,进而创建SubscriberMethod类,并将其添加到FindState类中的subscriberMethods 集合中;再此操作之前,FindState类会通过checkAdd方法来校验是否已经添加过此事件类型;

FindState类中的moveToSuperclass方法作用就是判断是否支持从父类查找,当FindState类中skipSuperClasses为true时,直接跳出循环,当skipSuperClasses为false时,将FindState类中calzz字段赋值为基类的类型,然后继续while循环查找,直到calzz类为系统的类型时,跳出循环;

当所有的循环查找结束后,执行getMethodsAndRelease方法,其实就是将 FindState类中的subscriberMethods字段返回,并且重置subscriberMethodFinder类中的FindState数组;

subscribe

当通过subscriberMethodFinder类获取到订阅者中所有的事件监听函数时,下一步就是方法的订阅;在subscribe方法中接收2个参数,一个是订阅者的实例,另外一个是订阅者中的监听事件处理函数的封装的SubscriberMethod类;

  private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
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);
break;
}
} List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType); if (subscriberMethod.sticky) {
if (eventInheritance) {
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);
}
}
}

subscribe方法的执行流程,收到就是通过SubscriberMethod中的eventType 获取事件类型,然后封装一个Subscription实例,我们知道EventBus类中维护这个一个key为事件类型,value为Subscription集合的

Map集合(subscriptionsByEventType),因此框架首先会从subscriptionsByEventType查找是否存在SubscriberMethod中事件类型,没有的话就创建一个CopyOnWriteArrayList<Subscription>subscriptions并添加到 subscriptionsByEventType集合中;

由于EventBus框架支持事件的排序,因此根据 subscriberMethod.priority 来进行排序添加;当EventBus框架接收到事件后,就会从subscriptionsByEventType取出所有支持事件类型的Subscription,并依此进行分发;

在框架进行subscribe时,还会将订阅者实例中所有支持的消息事件监听函数添加到EventBus类中的typesBySubscriber列表中,主要目的是当解除绑定时加快查找的效率;

EventBus框架是支持粘性事件的,EventBus 将最新的粘性事件保存在内存中,性事件可以被传递给订阅者或显示查询。因此,你不需要任何特殊逻辑去获取已有的数据。

当消息监听函数中的“Subscribe”注解中的sticky==true时,则这个监听函数就是个粘性事件的处理函数;在 EventBus框架中,在订阅者进行subscribe时就进行粘性事件的分发,由于粘性事件是存储在stickyEvents集合中,依此遍历去执行这个粘性事件;具体的事件分发方式是和普通的事件分发是一样的;

事件解绑

事件的解绑非常的简单,只需要调用EventBus框架的unregister方法就可以,在unregister方法内部,首先通过方法参数中的订阅者实例查找EventBus类中的typesBySubscriber列表,查找出所支持的所有支持的消息监听函数,然后在依此从subscriptionsByEventType列表中移除,最后从typesBySubscriber移除;

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 {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}

由浅入深了解EventBus:(四)的更多相关文章

  1. EventBus (四) Sticky事件

    什么是Sticky事件? 关于Sticky事件有的同学可能不是很熟悉,Sticky的意思是粘性的.在Android开 发中,Sticky事件只指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类 ...

  2. 由浅入深了解EventBus:(六)

    线程模型 在EventBus3.0框架中执行线程的快速切换,通过ThreadMode来指定线程在哪个线程中执行; 在EventBus3.0框架线程模型有个PendingPost 类负责数据的传递; f ...

  3. 由浅入深了解EventBus:(五)

    事件分发 EventBus3.0的事件的分发时通过EventBus类中的post(粘性事件为postSticky)方法,post与postSticky的唯一区别就是,在postSticky内部首先会向 ...

  4. 由浅入深了解EventBus:(三)

    原理 EventBus的核心工作机制如下图 在EventBus3.0架构图: EventBus类 在EventBus3.0框架的内部,核心类就是EventBus,订阅者的注册/订阅,解除注册,以及事件 ...

  5. 由浅入深了解EventBus:(二)

    概念 深入学习EventBus框架,就必须理解EventBus的相关原理和一些概念: Subscribe 在EventBus框架中,消息的处理接收方法必须要“@Subscribe”注解来进行标注: p ...

  6. 由浅入深了解EventBus:(一)

    概述 由greenrobot织贡献(该组织还贡献了greenDAO),一个Android事件发布/订阅轻量级框架; EventBus是一个消息总线,以观察者模式实现,用于简化程序的组件.线程通信,可以 ...

  7. mysql由浅入深探究(四)----mysql事务详解

    什么是事务: 通俗的解释就是对数据库进行的一组完整的操作,这组完整的操作中包含一个或多个操作.解释的太low了,来点官方的:事务就是DBMS中执行的一个完整的逻辑单元,这个逻辑单元中包含一个或者多个操 ...

  8. Android框架之EventBus的使用

    简介 EventBus是由greenrobot组织贡献的一个Android事件发布/订阅的轻量级框架.EventBus是一个Android端优化的publish/subscribe消息总线,简化了应用 ...

  9. quartz详解3:quartz数据库集群-锁机制

    http://blog.itpub.NET/11627468/viewspace-1764753/ 一.quartz数据库锁 其中,QRTZ_LOCKS就是Quartz集群实现同步机制的行锁表,其表结 ...

随机推荐

  1. ruby中的可调用对象--proc和lamdba

    ruby中将块转变成对象的三种方法 ruby中的大部分东西都是对象,但是块不是.那么,如果你想存下来一个块,方便以后使用,你就需要一个对象.ruby中有三种方法,把块转换成可以利用的对象. Proc. ...

  2. 48. Rotate Image(旋转矩阵)

      You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise) ...

  3. XVII Open Cup named after E.V. Pankratiev Grand Prix of Moscow Workshops, Sunday, April 23, 2017 Problem D. Great Again

    题目: Problem D. Great AgainInput file: standard inputOutput file: standard outputTime limit: 2 second ...

  4. java中hashSet原理

    转自: http://blog.csdn.net/guoweimelon/article/details/50804799 HashSet是JavaMap类型的集合类中最常使用的,本文基于Java1. ...

  5. java反射 - getXXX 与 getDeclaredXXX

    1.getXXX 和 getDeclaredXXX java 里 Class<?> 有下面这些方法: 类似的方法有: 2.getMethod(s) 和 getDeclaredMethod( ...

  6. rowspan && colspan

    > 跨行 <html> <body> <table width="> <tr> <th>col1</th> &l ...

  7. python---自动群发邮件

    生活中我们经常发送邮件,那么我们能不能用Python写一个自动发送邮件的功能呢?答案是肯定的!!! 开始实现功能之前我们需要开启我们邮箱的 IMAP/SMTP功能,我们先了解一下什么是IMAP/SMT ...

  8. [BZOJ1058]报表统计

    Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工 作,作为她的生日礼物之一.经过仔细观察,小Q发现统计一张报表实际上是维护一个 ...

  9. 转:MySQL 的show processlist

    processlist 命令的输出结果显示了有哪些线程在运行,可以帮助识别出有问题的查询语句,两种方式使用这个命令. 1.        进入 mysql/bin 目录下输入 mysqladmin p ...

  10. Protocol Buffers数据传输及存储协议简单使用

    我们知道Protocol Buffers是Google定义的一种跨语言.跨平台.可扩展的数据传输及存储的协议,因为将字段协议分别放在传输两端,传输数据中只包含数据本身,不需要包含字段说明,所以传输数据 ...