EventBus是Android下高效的发布/订阅事件总线机制。作用是可以代替传统的Intent,Handler,Broadcast或接口函数在Fragment,Activity,Service,线程之间传递数据,执行方法。特点是代码简洁,是一种发布订阅设计模式(Publish/Subsribe),或称作观察者设计模式。
EventBus 文档:http://greenrobot.org/eventbus/documentation/
一,定义事件:
定义一个事件,用于传递相应的事件信息,只要定义一个普通的Java类,无任何特殊要求。
public class SomeEvents{
private String mInfos ;
public SomeEvents(String infos){
mInfos=infos;
}
}
二,注册成订阅者:
对于Android来说,一般在Activity或Fragment的生命周期方法中进行注册,注册的同时,千万别忘了在相应的生命周期方法中反注册。
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
三,定义注册方法:
当一个事件发生时,对这个事件感兴趣的订阅者中相应的注册方法就会被调用,所以应该定义好相应的注册方法,对相应的事件进行处理。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventArrived(SomeEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
通过@Subscribe注解定义相应的方法,其中threadMode定义的是注册方法在那个线执行,一共有四个值,分别为POSTING、MAIN、BACKGROUND、ASYNC。
Thread.POSTING : 注册方法将会在事件被post出来的线程执行;
Thread.MAIN: 注册方法将会在主线程(UI线程)执行;
Thread.BACKGROUND: 如果事件被post出来的线程不是主线程,那么注册方法在此线程执行,否则在后台线程执行;
Thread.ASYNC: 注册方法将会在一个独立的线程执行;
四,Post事件:
可以调用以下方法来post出一个事件,从而使订阅者相应的注册方法被调用。
EventBus.getDefault().post(new SomeEvents("evetnsInfos"));
五,Sticky Events:
对于有些事件,可能后来者也会感兴趣,这样的事件被称为Sticky 事件,例如:地理位置。EventBus会维护每一个Sticky 事件的缓存,当有同类的事件来临时,缓存就会以被更新,同时如果在事件Post后有新的订阅都对Sticky 事件感兴趣,那么订阅者的相应方法(sticky =true)会立即被调用,不用再等到事件重新post。post Sticky事件时调用以下方法:
EventBus.getDefault().postStick(new SomeEvents("evetnsInfos"));
删除Sticky Events:SomeEvents stickyEvent=EventBus.getDefault().getStickyEvent(SomeEvents.class); if(stickyEvent != null) { EventBus.getDefault().removeStickyEvent(stickyEvent); }
或者:EventBus.getDefault().removeStickyEvent(SomeEvents.class);
六,优先级:
对于每个注册方法,都可以通过@Subscribe(priority = 1)来配置优先级,当然这种优先级是针对同一ThreadMode而言的。高优先级的注册方法能先接收到事件,并且可以通过调用EventBus.getDefault().cancelEventDelivery(event) 取消事件的传递。
七,源码分析:
一,首先来看EventBus.getDefault().register(Object obj)干啥了:
先看看EventBus几个重要变量的意义:
/**
* 每个事件与他的所有订阅者的映射关系
*/
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
/**
* 订阅者与它自己的订阅方法的映射关系
*/
private final Map<Object, List<Class<?>>> typesBySubscriber;
/**
* 存贮post过来的stickyEvents
*/
private final Map<Class<?>, Object> stickyEvents;
1,首先是通过EventBus.getDefault()得到了一个单例对象(单例模式)
/**
* Convenience singleton for apps using a process-wide EventBus instance.
*/
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
2,得到一个EventBus单例对象后调用register(Object obj)来注册订阅者的相关信息
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//通过Class对象找到相应类中定义的订阅方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//得到订阅者中的订阅方法后对方法的信息进行注册登记
subscribe(subscriber, subscriberMethod);
}
}
}
3,其中subscriberMethodFinder是一个工具类,正所谓名符其实。它主要的工作就是找到每个订阅者所有的订阅方法。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//如果已经注册过了就直接返回该类下所有的方法集合
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex 暂时是为false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
4,接着看findUsingInfo(Class<?> subscriberClass)
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
//通过 moveToSuperclass()方法更新 findState.clazz,即得到父类class ,直到为null
//如果父类已订阅了某一事件,那么子类理所应当的也有了相应的订阅方法
//即,EventBus支持了继承
while (findState.clazz != null) {
//这个方法暂时是返回空,因为findState.subscriberInfo与subscriberInfoIndexes均为空
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//跳转到这,检索当前类的订阅方法
findUsingReflectionInSingleClass(findState);
}
//检索父类
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
5,检索当前类的订阅方法: findUsingReflectionInSingleClass(findState);
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
//忽略非public , abstract,static ,bridge,synthetic的方法
//compilers may add methods. Those are called bridge or synthetic methods.
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
//EventBus的订阅方法有且只有一个参数
if (parameterTypes.length == 1) {
//得到相应的注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
//添加新的订阅方法
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}