Skip to content

Commit

Permalink
Added the ability to use @subscribe annotation
Browse files Browse the repository at this point in the history
 - This allows for cleaner code and more flexibility
 - with method naming
  • Loading branch information
Rick Brock committed Nov 7, 2014
1 parent 4cdd420 commit e42992a
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 40 deletions.
13 changes: 13 additions & 0 deletions EventBus/src/de/greenrobot/event/DeclarationType.java
@@ -0,0 +1,13 @@
package de.greenrobot.event;


public enum DeclarationType {
/**
* Declare via annotations
*/
ANNOTATIONS,
/**
* Declare via Method Naming
*/
METHOD_NAME
}
12 changes: 9 additions & 3 deletions EventBus/src/de/greenrobot/event/EventBus.java
Expand Up @@ -67,6 +67,7 @@ protected PostingThreadState initialValue() {

private boolean subscribed;
private boolean logSubscriberExceptions;
private final DeclarationType declarationType;

/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
Expand Down Expand Up @@ -100,11 +101,16 @@ public static void clearSkipMethodNameVerifications() {
SubscriberMethodFinder.clearSkipMethodVerifications();
}

public EventBus() {
this(DeclarationType.METHOD_NAME);
}

/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
public EventBus(DeclarationType decType) {
declarationType = decType;
subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
typesBySubscriber = new HashMap<Object, List<Class<?>>>();
stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
Expand Down Expand Up @@ -184,7 +190,7 @@ public void registerSticky(Object subscriber, String methodName) {

private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),
methodName);
methodName, declarationType);
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
Expand Down Expand Up @@ -226,7 +232,7 @@ private synchronized void register(Object subscriber, String methodName, boolean
Class<?>... moreEventTypes) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass,
methodName);
methodName, declarationType);
for (SubscriberMethod subscriberMethod : subscriberMethods) {
if (eventType == subscriberMethod.eventType) {
subscribe(subscriber, subscriberMethod, sticky, 0);
Expand Down
117 changes: 80 additions & 37 deletions EventBus/src/de/greenrobot/event/SubscriberMethodFinder.java
Expand Up @@ -26,12 +26,15 @@

import android.util.Log;

import de.greenrobot.event.annotations.Subscribe;

class SubscriberMethodFinder {
private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC;
private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>();
private static final Map<Class<?>, Class<?>> skipMethodVerificationForClasses = new ConcurrentHashMap<Class<?>, Class<?>>();

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String eventMethodName) {
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String eventMethodName,
DeclarationType declarationType) {
String key = subscriberClass.getName() + '.' + eventMethodName;
List<SubscriberMethod> subscriberMethods;
synchronized (methodCache) {
Expand All @@ -40,10 +43,10 @@ List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String ev
if (subscriberMethods != null) {
return subscriberMethods;
}

subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
Expand All @@ -55,44 +58,40 @@ List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String ev
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(eventMethodName)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(eventMethodName.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
ThreadMode threadMode = null;

if (declarationType == DeclarationType.METHOD_NAME) {
if (methodName.startsWith(eventMethodName)) {
threadMode = getThreadModeFromMethodName(clazz, method, eventMethodName);
} else {
continue;
}
} else if (declarationType == DeclarationType.ANNOTATIONS) {
if (method.isAnnotationPresent(Subscribe.class) && hasValidAccessModifiers(method)) {
Subscribe annotation = method.getAnnotation(Subscribe.class);
threadMode = annotation.threadMode();
} else {
continue;
}
}

/**
* So now we have a candidate method, if it has the proper access modifiers
* we add it to our list
*/
if (hasValidAccessModifiers(method)) {
Class<?> parameterType = validateAndGetMethodParameterType(method);
String methodKey = buildMethodKey(methodName, parameterType);
if (threadMode != null && eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, parameterType));
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}

clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {
Expand All @@ -106,6 +105,50 @@ List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String ev
}
}

private ThreadMode getThreadModeFromMethodName(Class<?> clazz, Method method, String eventMethodName) {
String modifierString = method.getName().substring(eventMethodName.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
return null;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}

return threadMode;
}

private boolean hasValidAccessModifiers(Method method) {
int modifiers = method.getModifiers();
return (modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0;
}

private Class<?> validateAndGetMethodParameterType(Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
return null;
}

return parameterTypes[0];
}

private String buildMethodKey(String methodName, Class<?> parameterType){
StringBuilder methodKeyBuilder = new StringBuilder();
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(parameterType.getName());
return methodKeyBuilder.toString();
}

static void clearCaches() {
synchronized (methodCache) {
methodCache.clear();
Expand Down
17 changes: 17 additions & 0 deletions EventBus/src/de/greenrobot/event/annotations/Subscribe.java
@@ -0,0 +1,17 @@
package de.greenrobot.event.annotations;


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import de.greenrobot.event.ThreadMode;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.PostThread;
}

0 comments on commit e42992a

Please sign in to comment.