前情概要

  在上一篇中,介绍了EventBus的基本使用方法,以及一部分进阶技巧。本篇及以后的几篇blog将会集中解析EventBus.java,看看作者是如何优雅地实现这个看似简单的事件分发/接收机制。

本篇概述

  剖析register的过程,let's get started!

方法签名

  完整的register方法签名如下,我们通常调用的register(this)其实最终调用到的register(this, false, 0),另外,使用registerSticky(this)进行调用,其实最终也是走到同一个方法中。

private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
  • 声明为synchronized,是因为其中调用的subscribe方法需要在同步块中执行;
  • 这个register方法其实只做了一件事:将目标class的所有onEvent方法取出(通过反射),逐一对事件订阅(通过subscribe)

  subscribe翻译过来就是“订阅、订购”,是本次重点解析的内容,下面来揭开她神秘的面纱:

subscribe/订阅

  对于熟悉Design Patterns的人来说,第一眼看到EventBus,脑海中浮现的一定是观察者模式/Observer,这个模式的实现方式,无非就是——事件的分发者拥有一个订阅者的列表,每次事件发生时,分发者遍历并通知列表中的订阅者。订阅者可以自由地加入/退出订阅列表。

  register是一个订阅的过程,故在subscribe中所做的无非就是“将目标类的目标方法加入到订阅者列表”这件事。让我们结合代码来看。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
} // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true); int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
} List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType); if (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);
}
}
}

  Event是分发者/订阅者解耦的关键,两者就是通过Event的class进行通信的。故先取出目标Event的订阅列表(subscriptionsByEventType),将新规则加入进去,如果重复register则会抛出Exception。加入的过程中,会根据优先级决定插入到List中的位置(int值越大,位置越靠前,优先级也就越高)。接下来还会将subscriber(也就是目标class)与新的eventType的对应关系加入到typesBySubscriber中,这个Map在后续的查找中会用到。

  对于sticky的的情况,若该eventBus支持事件继承(eventInheritance == true),则将eventType及其祖先类的所有Event都重新分发一遍,若不支持则只分发目标eventType。(此时分发的Event皆来自于stickyEvents

unregister/注销

  本篇最后分析一下注销的过程,代码很简单。

public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}

  unregister时,首先将eventtype - subscriber的绑定关系逐一解除(处理subscriptionsByEventType),最后把subscriber - eventTypes解绑。

下期预告

  剖析post过程

[EventBus源码解析] EventBus.register 方法详述的更多相关文章

  1. [EventBus源码解析] EventBus.post 方法详述

    前情概要 上一篇blog我们了解了EventBus中register/unregister的过程,对EventBus如何实现观察者模式有了基本的认识.今天我们来看一下它是如何分发一个特定事件的,即po ...

  2. EventBus源码解析 源码阅读记录

    EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...

  3. 【Android】EventBus 源码解析

    EventBus 源码解析 本文为 Android 开源项目实现原理解析 中 EventBus 部分项目地址:EventBus,分析的版本:ccc2771,Demo 地址:EventBus Demo分 ...

  4. Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean

    Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean 七千字长文深刻解读,Spirng中是如何初始化单例bean的,和面试中最常问的Sprin ...

  5. Android EventBus源码解析 带你深入理解EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...

  6. 源码解析-EventBus

    示例使用 时序图 源码解读 EventBus 使用 官网定义:EventBus 是一个使用 Java 写的观察者模式,解耦的 Android 开源库.EventBus 只需要几行代码即可解耦简化代码, ...

  7. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  8. [Java多线程]-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  9. EventBus源码解析

    用例 本文主要按照如下例子展开: //1. 新建bus对象,默认仅能在主线程上对消息进行调度 Bus bus = new Bus(); // maybe singleton //2. 新建类A(sub ...

随机推荐

  1. servlet执行流程

    视频地址:http://www.imooc.com/video/5550      1-6 用户输入地址:localhost:8080/MyFirstServletDemo/index.jsp (My ...

  2. Xcode5 取消ARC

    终于开心的装上Xcode5,主管马上发布新的任务,开始新的项目,各种开心,终于可以换个界面看看了. 可是谁知第一步创建项目就开始悲剧了,居然没有地方可以选择非ARC了,真是肿么个情况呀,查了一下,万能 ...

  3. [UML][转]UML类图符号 各种关系说明以及举例

    修改自:http://www.cnblogs.com/duanxz/archive/2012/06/13/2547801.html UML中描述类和类之间相互关系的方式包括:依赖(Dependency ...

  4. Python 中下划线

    1. 作为一个名称:在代码中使用一个名称,但是在后面的代码中不再会使用到的时候,就可以使用_作为临时名称. n = 42 for _ in range(n): do_something() 2. 名称 ...

  5. java中Collection类及其子类

    1:对象数组(掌握) (1)数组既可以存储基本数据类型,也可以存储引用类型.它存储引用类型的时候的数组就叫对象数组. 2:集合(Collection)(掌握) (1)集合的由来? 我们学习的是Java ...

  6. cocos2dx常见的46中+22中动作详解

    cocos2dx常见的46中+22中动作详解 分类: iOS2013-10-16 00:44 1429人阅读 评论(0) 收藏 举报 bool HelloWorld::init(){    ///// ...

  7. spring整合hibernate配置文件

    Spring对hibernate配置文件hibernate.cfg.xml的集成,来取代hibernate.cfg.xml的配置 spring对hibernate配置文件hibernate.cfg.x ...

  8. C++中 destory() 和deallocate()以及delete函数的相关性和区别性

    这里非常的绕口  需要仔细的来看看: destory(): 显示调用一个对象的析构函数 相当于释放一个对象需要释放的一些动态内存 为下次真正释放对象做准备 deallocate():真正的释放一个内存 ...

  9. 不同vlan之间的相互访问

    拓扑图: 用到的命令: 给端口的vlan <sw1>用户模式 切换到系统模式 system-view 交换机名称 sysname swj1 创建vlan 3 端口模式选择int g0/0/ ...

  10. java.lang.Enum<E extends Enum<E>>

    public enum Direction { L, LU, U, RU, R, RD, D, LD, STOP, JUMP;} for(Direction d: Direction.values() ...