事件总线模式
一、为何使用EventBus
今天我们介绍一个将耦合度降到极低的框架EventBus。
在这之前处理APP全局的监听,大多时候用的是BrodercastReceiver来实现时间的监听。但用起来很不方便,
BrodercastReceiver在APP中限定只监听系统的广播事件(如:电量,网络等)就OK了。在一些模块或组件之间的时间传递大多用的观察者,在使用的过程中你会发现一个个的回掉,各式各样的参数弄得你头晕目眩。那我们就接触好的框架解决开发中遇见的问题,让自己更轻松的开发。
二、EventBus原理分析
EventBus基于观察者模式的Android事件分发总线。通过事件总线可以实现Activity、Fragment、Service等不同模块或组件之间的通信。避免不同组件和模块之间的耦合问题。方便代码的维护与理解。
事件总线是基于观察者模式的思想实现的,它使用发布订阅的方式支持组件和模块间的通信,摒弃了观察者模式需要显示注册回调的缺点,同时可用于替换Java中传统的事件监听方式。
发布者Publisher:发布某种事件的对象。
事件Event:一个简单的POJO对象。只包含数据,不对数据进行处理。
事件总线EventBus: 负责存储时间信息,处理事件的流动和分发,通过时间总线,事件发布者与订阅者互相不知道对方的存在,是解耦的。事件被发布到总线上({@link #post(Object)})到总线,它将它传递给具有事件类型匹配处理程序方法的订阅服务器。要接收事件,订阅者必须使用{@link #register(Object)}向总线注册自己。注册之后,订阅者将收到事件,直到调用{@link #unregister(Object)}。事件处理方法必须由 {@link Subscribe}必须是public,返回非(空),并且只有一个参数 (事件)。
订阅者Subscriber: 订阅某个事件,一般情况会有一个回掉函数处理接收到的事件。订阅者通过{@link #register(Object)}向总线注册自己,即为订阅事件。订阅者也可以直到调用{@link #unregister(Object)}取消事件订阅。
三、EventBus开源库使用(来自greenrobot/EventBus)
优点:
1.简化组件之间的通信
2.分离事件发送器和接收器
3.对activity、fragment和线程进行良好的执行
4.避免复杂和容易出错的依赖关系和生命周期问题。
5.快速;专为高性能优化
6.体积小(<50k)
Android studio & Gradle使用分为以下几步:
0.在Gradle中添加在线依赖。
compile 'org.greenrobot:eventbus:3.0.0'
- 1
- 2
1.定义消息事件MessageEvent,也就是创建事件类型
public static class MessageEvent { /* Additional fields if needed */ }
- 1
2.选择你要订阅的订阅者(subscriber)在订阅者里需要用注解关键字 @Subscribe来“告诉”EventBus使用什么方法处理event。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
- 1
- 2
注册与注销订阅者。 通常在Android activity与fragment生命周期中注册与注销。
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3.发送事件。
EventBus.getDefault().post(new MessageEvent());
- 1
四、EventBus开源库源码解读
首先,解读@Subscribe,从源码可知,主要有三个参数ThreadMode(线程通信)、Sticky(粘性)、priority(事件优先级)。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
ThreadMode中有四种模式
1.ThreadMode.POSTING
默认的模式,推荐的模式。
订阅者将在同一线程中调用,该线程将发布事件。
此模式完全避免了线程切换,开销最少。
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
- 1
- 2
- 3
- 4
2.ThreadMode.MAIN
用户将在Android的主线程(有时称为用户界面线程)中调用。
如果发布线程是主线程,则将直接调用事件处理程序方法。
使用此模式的事件处理程序必须快速返回,以避免阻塞主线程。
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
- 1
- 2
- 3
- 4
3.ThreadMode.BACKGROUND
如果post事件的线程不是主线程,则事件处理程序方法将直接在发布线程中调用。
如果发布线程是主线程,则EventBus使用单个后台线程,该线程将按顺序执行所有事件。
使用此模式的事件处理程序避免耗时操作,以避免阻塞后台线程。
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
- 1
- 2
- 3
- 4
4.ThreadMode.ASYNC
事件处理方法在单独的线程中调用。
发布线程和主线程始终独立。
发布事件不会等待事件处理程序使用此模式。
如果事件处理程序的执行可能需要一些时间,例如网络访问,则事件处理程序方法应该使用此模式。
避免同时触发大量长时间运行的异步处理程序方法,从而限制并发线程的数量。
EventBus使用线程池有效地重用已完成的异步事件处理程序通知中的线程。
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
- 1
- 2
- 3
- 4
StickyEvent(粘滞事件)
StickyEvent的特点是,订阅者在发布事件之后订阅该事件,还是可以收到该事件。StickyEvent在内存中保存最新的消息,取消原有消息,执行最新消息,只有在注册后才会执行,如果没有注册,消息会一直保留来内存中。
//发送粘滞事件
EventBus.getDefault().postSticky(new MessageEvent());
- 1
- 2
//订阅事件,处理事件
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
- 1
- 2
- 3
- 4
- 5
//获取给定类型的最新粘滞事件。
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
if(stickyEvent != null) {
//移除并获取给定事件类型的最新粘滞事件。
EventBus.getDefault().removeStickyEvent(stickyEvent);
//移除全部的粘滞事件
// EventBus.getDefault().removeAllStickyEvents();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
removeStickyEvent源代码
/**
* Remove and gets the recent sticky event for the given event type.
*
* @see #postSticky(Object)
*/
public <T> T removeStickyEvent(Class<T> eventType) {
synchronized (stickyEvents) {
return eventType.cast(stickyEvents.remove(eventType));
}
}
/**
* Removes the sticky event if it equals to the given event.
*
* @return true if the events matched and the sticky event was removed.
*/
public boolean removeStickyEvent(Object event) {
synchronized (stickyEvents) {
Class<?> eventType = event.getClass();
Object existingEvent = stickyEvents.get(eventType);
if (event.equals(existingEvent)) {
stickyEvents.remove(eventType);
return true;
} else {
return false;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
priority事件优先级
用户优先级影响事件传递的顺序。
在同一传递线程({@link ThreadMode})内,更高优先级的订阅者将在其他优先级较低的其他事件之前接收事件。
默认优先级为0。
注意:优先级不影响不同{@link ThreadMode}的订阅者之间的传递顺序!
//priority越大,优先级越高
@Subscribe(priority = 1)
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
- 1
- 2
- 3
- 4
- 5