事件注册

在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. JForum的运行环境

    JForum的运行环境: 开始本文之前,我们确认一下JForum的运行环境. - Java动态运行环境(JRE) - 支持J2EE Servlet标准的任何一款Web服务器:Tomcat,JBoss, ...

  2. 懒加载 js----例子------图片

    转载自:https://www.jianshu.com/p/9b30b03f56c2 懒加载工具类 <script type="text/javascript"> // ...

  3. Jmeter使用流程及简单分析监控(转载)

    转载自:https://www.cnblogs.com/linglingyuese/archive/2013/03/04/linglingyuese-one.html#undefined 一.安装Jm ...

  4. 通过Java编码获取String分行字符串的内容

    代码案列: import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException ...

  5. python3_Logging模块详解

    python的logging模块提供了通用的日志系统,可以方便第三方模块或应用使用. 简单使用 import logging # logging.config.fileConfig("./l ...

  6. 关于Linq翻译Inner join ,Left join (本文为转载)

    我們先來一段最基本的 LINQ to SQL 使用類似 T-SQL 的 INNER JOIN 資料查詢語法: from c in Categories from o in c.Products sel ...

  7. Linux文件系统学习(一)之相关概念⭐⭐⭐

    “一切皆是文件”是 Unix/Linux 的基本哲学之一.不仅普通的文件,目录.字符设备.块设备.套接字等在 Unix/Linux 中都是以文件被对待:它们虽然类型不同,但是对其提供的却是同一套操作界 ...

  8. spring cron表达式及解析过程

    1.cron表达式 cron表达式是用来配置spring定时任务执行时间的字符串,由5个空格分隔成的6个域构成,格式如下: {秒}  {分}  {时}  {日}  {月}  {周} 每一个域的含义解释 ...

  9. linux修改单个进程的系统时间

    简介 如下是 libfaketime 的一个简单实例. 在工作中常常需要测试修改时间,如果环境不允许调整时间,就要想办法调整单个进程的时间了. 编译安装 git clone https://githu ...

  10. LeetCode——Decode String

    Question Given an encoded string, return it's decoded string. The encoding rule is: k[encoded_string ...