EventBus 3.0 源码分析

功能

EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。

使用

EventBus 3.0 源码分析_第1张图片
01.png

这是GitHub,EventBus的使用介绍,用起来还是很简单的。

类图

EventBus 3.0 源码分析_第2张图片
02.png

从上面的类图可以看出,都是围绕类EventBus 通过组合来建立关联。
可以看到优秀的框架设计,多用组合,少用继承。

源码分析

EventBus.getDefault();

获取Event Bus的实例:

EventBus 3.0 源码分析_第3张图片
03.png

使用单例模式来创建出Event Bus对象,采用了双重验证 以保证线程安全。,看看Event Bus的构造函数。

EventBus 3.0 源码分析_第4张图片
04.png

在构造函数里进行了一系列的初始化工作,主要通过类EventBusBuilder 使用Build的模式来初始化EventBus的一些配置。

还有几个重要的数据结构集合的初始化:

subscriptionsByEventType

订阅该事件的所有订阅者 。是一个以 key:订阅的事件 value:订阅这个事件的所有订阅者。因此当post的时候会遍历这个Map相应对应事件的List。

typesBySubscriber

订阅者中的所有订阅事件 。是一个以key:订阅者 value:订阅者中的所有订阅事件。用于后续的取消订阅。

三个Poster类

05.png

Poster类是用来处理sticky事件。

register(this)

EventBus 3.0 源码分析_第5张图片
06.png

委托SubscriberMethodFinder类,查找出订阅者中的所有订阅事件方法。进入findSubscriberMethods方法看看:

EventBus 3.0 源码分析_第6张图片
07.png

该方法的处理流程大概是这样的:

EventBus 3.0 源码分析_第7张图片
09.png

这里我们只分析 通过findUsingReflection 方法,通过反射获取订阅者中的所有订阅事件方法。进去看看这个方法:

EventBus 3.0 源码分析_第8张图片
11.png

查找到的订阅事件会在临时的类FindState中做校验和保存,为什么会提到这个临时类呢?这里有一个很好的设计,使用了“对象池”的概念来创建FindState对象,和线程池同一个概念, 这样可以使对象FindState复用,防止创建过多的对象,增加内存开销。

去看看如何复用FindState对象的,进入上图prepareFindState方法:

EventBus 3.0 源码分析_第9张图片
12.png

在112中 可以看到 会从对象池FIND_STATE_POOL取出FindState 而不是每次都创建对象。

回到 findUsingReflection方法当查找完所有的订阅事件方法,调用getMethodsAndRelease对FindState进行回收复用:

EventBus 3.0 源码分析_第10张图片
13.png

还是回到 findUsingReflection 方法,调用了findUsingReflectionInSingleClass 方法来进行查找订阅事件方法,进去看看是怎么查找的:

EventBus 3.0 源码分析_第11张图片
14.png
  1. 在154行 通过反射 获取订阅者(this)的所有方法。
  2. 在160 行遍历这些方法。
  3. 在164行 判断是否带@Subscribe 注解的方法 是否只有一个参数,如果否 就会抛出异常
  4. 在165行判断方法是否带@Subscribe 注解 ,如果是,通过FindState对象进行校验和保存。

其实很简单 就是通过反射来查找到订阅者的所有方法,查找出带@Subscribe注解的方法保存起来。

回到register方法,查找完所有的订阅者中的所有订阅事件方法,之后遍历列表,进入subscribe方法:

EventBus 3.0 源码分析_第12张图片
16.png
  1. 在146行 获取订阅方法的订阅类型(就是带@Subscribe 注解的方法的第一个参数)。
  2. 封装Subscription对象,保存到subscriptionsByEventType数据集合中。
  3. 在159行,根据优先级 把封装Subscription对象,保存到typesBySubscriber数据集合中。
  4. 在174行 如果是sticky事件,立即post sticky事件到当前订阅者。

整个register的流程大概就是这样:

EventBus 3.0 源码分析_第13张图片
17.png

post 事件

最后通过post方法 来发送事件,进入post方法

EventBus 3.0 源码分析_第14张图片
18.png

首先从currentPostingThreadState 获取 PostingThreadState 对象

currentPostingThreadState 是个ThreadLocal对象

19.png

ThreadLocal使得各线程能够保持各自独立的一个对象。
把要post的事件加入PostingThreadState 的eventQueue队列中,循环取出事件。
进入postSingleEvent方法

EventBus 3.0 源码分析_第15张图片
20.png
  1. 在365行,判断是否触发订阅该事件的父类,接口的方法。
  2. 调用postSingleEventForEventType 方法分发事件。

进入postSingleEventForEventType方法

EventBus 3.0 源码分析_第16张图片
21.png
  1. 在389行,从subscriptionsByEventType数据集合中取出该订阅事件的所有订阅者。
  2. 在392行,分发所有的订阅者,通过调用postToSubscription方法。

进入postToSubscription方法

EventBus 3.0 源码分析_第17张图片
22.png

根据不同的threadMode 在不同的线程中处理,最终都会调用invokeSubscriber方法,把事件invoke到订阅者的方法中。

EventBus 3.0 源码分析_第18张图片
23.png

ThreadMode 共有四类:

  1. PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作
  2. MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread
    类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作
  3. BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
  4. Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问

引用出自

unregister

EventBus 3.0 源码分析_第19张图片
25.png

从 typesBySubscriber 数据集合中取出该订阅者的所有订阅方法,remove所有订阅者的订阅方法,防止内存泄漏。

整个post流程:

EventBus 3.0 源码分析_第20张图片
24.png

最后

EventBus 属于一个比较容易理解的开源库,项目整体的框架设计采用了观察者模式,但不论从使用方式和实现方式上都是非常值得我们学习的开源项目。比如,对象池的设计,三大Poster类的设计,多用组合,少用继承,缓存的设计等都值得我们借鉴和使用。

END。

你可能感兴趣的:(EventBus 3.0 源码分析)