一 在项目中使用EventBus
首先是在项目中导入EventBus,最新版本3.1.1(更新于2017年)
implementation 'org.greenrobot:eventbus:3.1.1'
在Activity中,一般是在onStart()方法中register EventBus,在onStop()中unregister。 有些项目中,想要Activity在后台时,也可以订阅,可以在onCreate(),onDestroy()中register,unregister。
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
EventBus在这里使用了一个单例模式,通过getDefault()获取单例对象。
后面我们设置两个ImageView的点击事件,通过post方法发送不同的Event。(其中的两个Event不在这里贴代码,其中主要是有一个String变量保存信息)
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send0:
Send0ClickEvent msg = new Send0ClickEvent();
msg.setMsg("Hello World");
EventBus.getDefault().post(msg);
break;
case R.id.send1:
Send1ClickEvent msg1 = new Send1ClickEvent();
msg1.setMsg("你好 世界");
EventBus.getDefault().post(msg1);
default:
break;
}
}
再写订阅的方法。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onClickSend0Event(Send0ClickEvent event){
Toast.makeText(this, "onClickSend0Event " + event.getMsg(), Toast.LENGTH_SHORT).show();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onClickSend1Event(Send1ClickEvent event){
Toast.makeText(this, "onClickSend1Event " + event.getMsg(), Toast.LENGTH_SHORT).show();
}
最终,点击send0和send1会分别postEvent,通过Event Type (如上面代码中的Send0ClickEvent和Send1ClickEvent这两种类型的Event)分发给不同的订阅方法。
二 分析EventBus的源码
1.register()
/**
* 注册订阅者接受事件,register()之后,当不需要订阅时必须要调用unregister()
* 订阅者中必须有方法使用@Subscribe注释修饰,在注释中可以配置ThreadMode和priority
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
通过findSubscriberMethods获取订阅者中使用了@Subscribe的方法。下面看看findSubscriberMethods的代码。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//METHOD_CACHE是一个全局缓存。
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
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 {
//在这里将获取的subscriberMethods缓存到内存中。
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
首先在METHOD_CACHE中查询有没有已经保存的subscriberMethods,有就直接返回。所以,一般只有第一次注册是比较花费时间的。后面判断ignoreGeneratedIndex,如果没设置过,默认是false,调用了findUsingInfo,是在后面使用getDeclaredMethods/getMethod来遍历这个类的所有方法。如果设置了编译期获取订阅者信息,会调用findUsingReflection。在这里只分析findUsingInfo,下面是代码。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//在EventBus中会维护一个FindState数组。prepareFindState只是从FindState数组中拿一个出来用。
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
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);
}
//在这里就可以知道while (findState.clazz != null)的意思了,所有的父类都会执行一次上面的代码。
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
首先获取一个FindState对象,用来存储后面获取到的结果,在这里FindState是循环使用的,每次获取到结果后会重置。后面的findState.subscriberInfo没设置过的话为null,所以后面是调用到findUsingReflectionInSingleClass方法。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {getMethods
// 像Activity这种很多方法的Class,getDeclaredMethods方法比getMethods更快。
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等修饰
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
//必须只有一个入参
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
//必须使用@Subscribe注释
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
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");
}
}
}
在上面代码中,获取订阅者中符合条件的方法,最终将符合的方法都存储在findState中(包括了方法 event类型 注释中包含的信息)。 最终获取到了调用register()方法时传入的class参数中使用了@Subscribe注释的方法。
再回看register的代码
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
将获取到的方法遍历,调用subscribe方法。然后我们看看这个方法是做了什么。
// 必须在synchronized代码块中调用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//subscription中包含了订阅者这个对象的引用、和订阅的方法。
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//从subscriptionsByEventType这个map中获取subscriptions,map的key为eventType。
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
//注释中如果有给priority赋值,则会在这里起作用,这里priority大的优先级高,会在List的前面。如果优先级相等,则按照先后顺序。
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//typesBySubscriber也是一个map,key也是订阅者这个对象的引用,value是这个订阅者中所有的eventType。
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//sticky这个值是调用postSticky()方法才会为true。postSticky()方法与post()方法类似,唯一不同是可以在register前调用,然后会将post的事件暂时存储起来,
//在register时(也就是这里)再去执行。
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
这个方法将前面获取的methods信息都放到两个map中,subscriptionsByEventType这个map的key是eventType,value是subscrition列表(一个subscrition包含了订阅者,以及一个订阅方法这些信息),typesBySubscriber这个map的key是订阅者,value是这个订阅者的所有订阅的eventType。最后还有关于sticky的代码,在这里不过多赘述。
至此register的代码分析完了,主要就是将订阅者传进来,然后根据class获得订阅的方法,最后将这些信息都存储到两个全局map中。