EventBus3.0官方使用文档
EventBus3.0GitHub地址
在EventBus3.0的官方文档中,我们可以从下面几个步骤去使用EventBus来实现事件响应机制。
上图是官方的角色协作图,可以看出EventBus有一下几个角色
在gradle中配置EventBus
compile 'org.greenrobot:eventbus:3.0.0'
之后我们定义一个简单的Event
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
订阅者事件就是事件触发时响应的方法,这些方法用@Subscribe 注解声明。
// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
当然,订阅者是需要在EventBus中注册和解除注册,才能完成一个正常的流程,一般推荐在Activity和Fragment的onStart和onStop生命周期方法中进行Subscribe的注册和解除注册。
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
在代码中发送一个Event事件,当前所有订阅了该事件的订阅者都能响应这个事件。
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
ThreadMode是用来控制Subscribe方法被调用时的线程,它有以下几种选择方式
即onMessage方法在跟发送方法(post)同一个线程被调用,这种模式意味着最少的开销,因为它完全避免了线程切换。所以在一些耗时较短的任务可以选择这种模式。这也是默认的ThreadMode。
// Called in the same thread (default)
// ThreadMode is optional here
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
log(event.message);
}
显而易见,这种模式下响应方法会在Android主线程(UI)被调用。故不赘述。
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
Subscribers方法 会在一个后台线程被调用。这种模式下分两种情况,
一、当post方法不在主线程时,Subscribers方法 会直接在跟post同一个线程被调用。
二、当post方法在主线程时,
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
这种模式下,事件处理方法在一个单独的线程中被调用。不会阻塞posting的线程,为了避免触发大量的长时间运行的异步Handler方法同时限制并发线程的数量,EventBus使用线程池来重用线程处理异步事件响应。
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
按照订阅事件的优先级高低,来决定同类型响应事件方法的调用顺序。
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100) //在ui线程执行 优先级100
public void onDataSynEvent(DataSynEvent event) {
Log.e(TAG, "event---->" + event.getCount());
}
跟广播的传递规则类似,高优先级的响应方法可以终止事件的传递
EventBus.getDefault().cancelEventDelivery(event) ;//优先级高的订阅者可以终止事件往下传递
发送StickyEvent:
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
接受StickyEvent:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.message);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
解除注册:
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// "Consume" the sticky event
EventBus.getDefault().removeStickyEvent(stickyEvent);
// Now do something with it
}
EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析,
处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的
信息速度要快.
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
}
}
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
如果上面的配置不起作用,可以使用apt配置
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
eventBusIndex "com.example.myapp.MyEventBusIndex"
}
}
配置完成后 rebuild工程后,就会在\build\generated\source\apt\PakageName\目录生成MyEventBusIndex 索引,
添加索引:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusAppIndex())
.addIndex(new MyEventBusLibIndex()).build();
首先是调用post方法发送事件
/** Posts the given event to the event bus. */
public void post(Object event) {
//得到当前线程的Posting状态.
PostingThreadState postingState = currentPostingThreadState.get();
//获取当前线程的事件队列
List
post() 方法首先从 currentPostingThreadState 对象中取了一个 PostingThreadState ,我们来看看这个 currentPostingThreadState 对象的创建代码
private final ThreadLocal currentPostingThreadState = new
ThreadLocal() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。 ThreadLocal 所包裹的是一个 PostingThreadState 类,它仅仅是封装了一些事件发送中过程所需的数据。
final static class PostingThreadState {
//通过post方法参数传入的事件集合
final List
接下来我们看下发送事件的核心方法,postSingleEvent:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
//是否触发订阅了该事件(eventClass)的父类,以及接口的类的响应方法.
if (eventInheritance) {
//查找eventClass类所有的父类以及接口
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
//循环postSingleEventForEventType
for (int h = 0; h < countTypes; h++) {
Class> clazz = eventTypes.get(h);
//只要右边有一个为true,subscriptionFound就为true
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//post单个
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//如果没发现
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
//发送一个NoSubscriberEvent事件,如果我们需要处理这种状态,接收这个事件就可以了
post(new NoSubscriberEvent(this, event));
}
}
}
继续查看postSingleEventForEventType方法
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList subscriptions;
//获取订阅了这个事件的Subscription列表.
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
//是否被中断
boolean aborted = false;
try {
//分发给订阅者
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
获取订阅了这个事件的Subscription列表,然后根据订阅者的ThreadMode,按照不同的线程切换方式依次分发给订阅者
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
这里是用到了反射机制,调用响应事件的方法,并将event作为参数传递过去。