1 EventBus简介
1.1 什么是EventBus?
EventBus是Android和java的发布/订阅事件总线。
1.2 作用
事件总线是对发布-订阅模式的一种实现,它是一种集中式事件处理机制,允许不同的组件之间彼此通信,而又不需要相互依赖,达到解耦的目的。
1.3 优点
(1)简单Android组件之间的通信,避免了Android四大组件复杂的生命周期处理;
(2)在Android四大组件间,以及线程间传递大数据时的唯一选择。因为序列化大数据进行传递时,是十分耗时缓慢的,用EventBus是最优解法;
(3)使用class传递数据,可以携带各种各样的数据,摆脱了用Bundle传递list和数组的麻烦;
(4)清晰明了的主子线程,让你的代码更为简洁;
1.4 缺点
(1)EventBus可以大量解耦项目,但是滥用它,会产生一个非常危险的后果:需要定义大量的常量或者新的实体类来区分接收者,管理EventBus的消息类别将会让你很痛苦;
(2)不要在非前台组件中使用它,因为将它使用到每一个工具类或者后台业务类,会让数据发送与接收更加复杂。别忘记了Java本身就是面对对象语言,它有接口、抽象可以实现来发送与接收数据。我们可以用各种设计模式,比如观察者模式,来更好的优化与独立自己的业务,不需要依赖EventBus;
(3)EventBus,并不是真正的解耦,不要在独立的模块里使用EventBus来分发。这个模块如果那天要直接放入另外一个项目里,你怎么解耦EventBus? 最好,还是多使用接口与Activity本身的数据传递方式。。
1.5 EventBus2.x与EventBus3.x的版本有哪些区别呢?
(1)EventBus2.x使用的是运行时注解,它采用了反射的方式对整个注册的类的所有方法进行扫描来完成注册,因而会对性能有一定影响;
(2)EventBus3.x使用的是编译时注解,Java文件会编译成.class文件,再对class文件进行打包等一系列处理。在编译成.class文件时,EventBus会使用EventBusAnnotationProcessor注解处理器读取@Subscribe()注解并解析、处理其中的信息,然后生成Java类来保存所有订阅者的订阅信息。这样就创建出了对文件或类的索引关系,并将其编入到apk中;
(3)从EventBus3.0开始使用了对象池缓存减少了创建对象的开销;
2 EventBus使用3步骤
(1)定义事件
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
(2)准备订阅者
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
// 这个方法将在MessageEvent被发布时被调用(在Toast的UI线程中)
@Subscribe (threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// 这个方法将在SomeOtherEvent发布时被调用
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
(3)发布事件
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
(4)学习链接
如何开始与EventBus在3个步骤
3 EventBus原理
3.1 最核心的原理
利用了subscriptionsByEventType这个重要的HashMap对象,将订阅者们,即接收事件的方法存储在这个列表,发布事件的时候在列表中查询出相对应的方法并执行。
Map, CopyOnWriteArrayList> subscriptionsByEventType;
3.2 EventBus框架原理

3.2.1 关键对象分析
(1)Publisher是发布者:通过post()方法将消息事件Event发布到事件总线;
(2)EventBus是事件总线: 核心模块;
(3)Subscriber是订阅者:收到事件总线发下来的消息,即onEvent方法被执行。注意参数类型必须和发布者发布的参数一致;
3.2.2 关键方法的分析
(1)register(Object object): 将所在类作为订阅者,框架会通过反射机制获取所有方法以及其参数,并将订阅者和订阅的方法以及其参数保存到Map中,key为Object,value为Subscription类型的ArrayList,下次从Map中获取。
(2)post(Object event):去遍历所有已经注册事件的订阅者们(即是Map中的key值),然后在根据订阅者对象找到它内部被注解方法(onEvent()),再匹配“被注解方法的参数类型”和“发布者发送的事件类型”是否是同一类型或者父类,就进行反射调用。线程切换用handler和线程池结合runnable进行切换。。
(3)postSticky(Object event): 最后还是调用的post()方法,只是多加了一个添加到粘滞Map中(Map, Object> stickyEvents)步骤;每次调用register(this)时,会检查粘滞Map中是否有订阅对象,然后进行发送;粘滞事件接收后一定要记得移除。
4 造轮子的方式分析EventBus原理
4.1 基本实体类
// 订阅者类的方法实体
public class SubscriberMethod {
/** 回调方法 */
public Method method;
/** 线程模式 */
public ThreadMode threadMode;
/** 方法中的参数 */
public Class eventType;
/** 是否是粘滞事件 */
public final boolean sticky;
public SubscriberMethod(Method method, ThreadMode threadMode, Class eventType, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.sticky = sticky;
}
}
// 包装了订阅者+订阅者类的方法的对象
public class Subscription {
public final Object subscriber;
public final SubscriberMethod subscriberMethod;
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
}
@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
@Override
public int hashCode() {
return subscriber.hashCode() + subscriberMethod.eventType.hashCode();
}
}
// 包装了订阅者+订阅者类的方法的对象 + 事件对象
public class PendingPost {
public Object event;
public Subscription subscription;
public PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
}
4.2 辅助类
// 方法执行的线程
public enum ThreadMode {
/**
* 订阅者将在发布事件的同一线程中被直接调用。这是默认值。事件传递意味着开销最小,因为它完全避免了线程切换。
* 因此,对于可以在很短的时间内完成而不需要主线程的简单任务,推荐使用这种模式。使用此模式的事件处理程序必须快速返回,
* 以避免阻塞可能是主线程的发布线程。
*/
POSTING,
/**
* 订阅者将在Android的主线程(UI线程)中被调用。如果发布线程是主线程、订阅方方法将被直接调用,阻塞发布线程。
* 否则事件正在排队等待交付(非阻塞)。使用此模式的订阅者必须快速返回,以避免阻塞主线程。
*/
MAIN,
/**
* 订阅者将在后台线程中被调用。如果发布线程不是主线程,订阅方方法将在发布线程中直接调用。
* 如果提交线程是主线程,则EventBus使用单个线程后台线程,它将按顺序交付其所有事件。
* 使用此模式的订户应尝试这样做快速返回,避免阻塞后台线程。
*/
BACKGROUND
}
// 注解类,只有被注解订阅的方法才会被添加到集合中
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
/** 线程模式 */
ThreadMode threadMode() default ThreadMode.POSTING;
/** 如果为true,则post最近的粘滞事件 */
boolean sticky() default false;
}
// 判断是否是主线程,通常在Android上使用Android的主线程
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper);
}
}
}
4.3 线程切换发布事件类
// post事件接口
interface Poster {
/**
* 为特定订阅加入要发布的事件队列
*
* @param subscription 订阅将会收到事件的订阅
* @param event 事件将会发布给订阅者.
*/
void enqueue(Subscription subscription, Object event);
}
// 主线程中post事件
public class HandlerPoster extends Handler implements Poster {
private final LinkedList queue;
private final EventBus eventBus;
private boolean handlerActive;
protected HandlerPoster(EventBus eventBus, Looper looper) {
super(looper);
this.eventBus = eventBus;
this.queue = new LinkedList<>();
}
public void enqueue(Subscription subscription, Object event) {
synchronized (this) {
PendingPost pendingPost = new PendingPost(event, subscription);
queue.offer(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
Log.e(EventBus.TAG, "Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// 再次检查,这次是同步的
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost.subscription, pendingPost.event);
rescheduled = true;
}
} finally {
handlerActive = rescheduled;
}
}
}
// 子线程中post事件
public class BackgroundPoster implements Runnable, Poster {
private final LinkedList queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new LinkedList<>();
}
public void enqueue(Subscription subscription, Object event) {
synchronized (this) {
PendingPost pendingPost = new PendingPost(event, subscription);
queue.offer(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// 再次检查,这次是同步的
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost.subscription, pendingPost.event);
}
} finally {
executorRunning = false;
}
}
}
4.4 EventBus
/**
* EventBus发布/订阅事件总线
*/
public class EventBus {
public static String TAG = "Tag_EventBus";
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
private static volatile EventBus defaultInstance;
/**
* 判断是否是主线程
*/
private final MainThreadSupport mainThreadSupport;
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
/**
* 接收 订阅者+订阅者类的方法的对象 存储在这个列表中
*/
private final Map