Skip to content

Commit

Permalink
work around for broken Class.getDeclaredMethods() in Android (should fix
Browse files Browse the repository at this point in the history
  • Loading branch information
greenrobot committed Nov 2, 2015
1 parent e25cfa4 commit be04a2d
Showing 1 changed file with 71 additions and 42 deletions.
113 changes: 71 additions & 42 deletions EventBus/src/de/greenrobot/event/SubscriberMethodFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -62,7 +61,7 @@ List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
HashMap<String, Class> eventTypesFound = new HashMap<String, Class>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
Expand All @@ -72,46 +71,18 @@ List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
}

// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
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(ON_EVENT_METHOD_NAME.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);
}
}
try {
// This is faster than getMethods, especially when subscribers a fat classes like Activities
Method[] methods = clazz.getDeclaredMethods();
filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);
} catch (Throwable th) {
th.printStackTrace();
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
Method[] methods = subscriberClass.getMethods();
subscriberMethods.clear();
eventTypesFound.clear();
filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);
break;
}
clazz = clazz.getSuperclass();
}
Expand All @@ -126,6 +97,64 @@ List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
}
}

private void filterSubscriberMethods(List<SubscriberMethod> subscriberMethods,
HashMap<String, Class> eventTypesFound, StringBuilder methodKeyBuilder,
Method[] methods) {
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
Class<?> methodClass = method.getDeclaringClass();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
ThreadMode threadMode = getThreadMode(methodClass, method, methodName);
if (threadMode == null) {
continue;
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class methodClassOld = eventTypesFound.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
} else {
// Revert the put, old class is further down the class hierarchy
eventTypesFound.put(methodKey, methodClassOld);
}
}
} else if (!skipMethodVerificationForClasses.containsKey(methodClass)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + methodClass + "."
+ methodName);
}
}
}
}

private ThreadMode getThreadMode(Class<?> clazz, Method method, String methodName) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.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)) {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
} else {
threadMode = null;
}
}
return threadMode;
}

static void clearCaches() {
synchronized (methodCache) {
methodCache.clear();
Expand Down

0 comments on commit be04a2d

Please sign in to comment.