Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflection-less Index generation [Upto 70% improvement in performance] #395

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ android:
- extra-android-m2repository

before_script:
- chmod +x gradlew
# - echo no | android create avd --force -n test -t android-10 --abi armeabi
# - emulator -avd test -no-skin -no-audio -no-window &
# - android-wait-for-emulator
Expand Down
3 changes: 2 additions & 1 deletion EventBus/src/org/greenrobot/eventbus/EventBus.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public EventBus() {
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();

List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
Expand Down Expand Up @@ -482,7 +483,7 @@ void invokeSubscriber(PendingPost pendingPost) {

void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
subscription.subscriberMethod.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.greenrobot.eventbus;

import org.greenrobot.eventbus.meta.SubscriberMethodInvoker;

import java.lang.reflect.InvocationTargetException;

/** Used internally by EventBus for generated index */
public class GeneratedSubscriberMethod extends SubscriberMethod{

final SubscriberMethodInvoker invoker;
final String methodName;
final Class<?> declaringClass;

public GeneratedSubscriberMethod(SubscriberMethodInvoker invoker, String methodName, Class<?> declaringClass, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
super(eventType, threadMode, priority, sticky);
this.invoker = invoker;
this.methodName = methodName;
this.declaringClass = declaringClass;
}

@Override
public void invoke(Object subscriber, Object event) throws InvocationTargetException, IllegalAccessException {
invoker.invoke(subscriber, event);
}

@Override
public String getName() {
return methodName;
}

@Override
public Class<?> getDeclaringClass() {
return declaringClass;
}

@Override
public int hashCode() {
return this.methodString.hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.greenrobot.eventbus;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/** Used internally by EventBus */
public class ReflectiveSubscriberMethod extends SubscriberMethod{
final Method method;

public ReflectiveSubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
super(eventType, threadMode, priority, sticky);
this.method = method;
}

@Override
public void invoke(Object subscriber, Object event) throws InvocationTargetException, IllegalAccessException {
method.invoke(subscriber, event);
}


@Override
public String getName() {
return method.getName();
}

@Override
public Class<?> getDeclaringClass() {
return method.getDeclaringClass();
}

@Override
public int hashCode() {
return method.hashCode();
}
}
23 changes: 12 additions & 11 deletions EventBus/src/org/greenrobot/eventbus/SubscriberMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,18 @@
*/
package org.greenrobot.eventbus;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

/** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
final Method method;
public abstract class SubscriberMethod {
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;

public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
public SubscriberMethod(Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
Expand All @@ -54,15 +52,18 @@ private synchronized void checkMethodString() {
if (methodString == null) {
// Method.toString has more overhead, just take relevant parts of the method
StringBuilder builder = new StringBuilder(64);
builder.append(method.getDeclaringClass().getName());
builder.append('#').append(method.getName());
builder.append(getDeclaringClass().getName());
builder.append('#').append(getName());
builder.append('(').append(eventType.getName());
methodString = builder.toString();
}
}

@Override
public int hashCode() {
return method.hashCode();
}

public abstract void invoke(Object subscriber, Object event) throws InvocationTargetException, IllegalAccessException;


public abstract String getName();

public abstract Class<?> getDeclaringClass();
}
19 changes: 10 additions & 9 deletions EventBus/src/org/greenrobot/eventbus/SubscriberMethodFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
if (findState.checkAdd(subscriberMethod, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
Expand Down Expand Up @@ -165,10 +165,11 @@ private void findUsingReflectionInSingleClass(FindState findState) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
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()));
ThreadMode threadMode = subscribeAnnotation.threadMode();
ReflectiveSubscriberMethod reflectiveSubscriberMethod = new ReflectiveSubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky());
if (findState.checkAdd(reflectiveSubscriberMethod, eventType)) {
findState.subscriberMethods.add(reflectiveSubscriberMethod);
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
Expand Down Expand Up @@ -216,15 +217,15 @@ void recycle() {
subscriberInfo = null;
}

boolean checkAdd(Method method, Class<?> eventType) {
boolean checkAdd(SubscriberMethod method, Class<?> eventType) {
// 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.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
if (existing instanceof SubscriberMethod) {
if (!checkAddWithMethodSignature((SubscriberMethod) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
Expand All @@ -235,7 +236,7 @@ boolean checkAdd(Method method, Class<?> eventType) {
}
}

private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
private boolean checkAddWithMethodSignature(SubscriberMethod method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package org.greenrobot.eventbus.meta;

import org.greenrobot.eventbus.EventBusException;
import org.greenrobot.eventbus.GeneratedSubscriberMethod;
import org.greenrobot.eventbus.ReflectiveSubscriberMethod;
import org.greenrobot.eventbus.SubscriberMethod;
import org.greenrobot.eventbus.ThreadMode;

Expand Down Expand Up @@ -58,23 +60,5 @@ public boolean shouldCheckSuperclass() {
return shouldCheckSuperclass;
}

protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType) {
return createSubscriberMethod(methodName, eventType, ThreadMode.POSTING, 0, false);
}

protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode) {
return createSubscriberMethod(methodName, eventType, threadMode, 0, false);
}

protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode,
int priority, boolean sticky) {
try {
Method method = subscriberClass.getDeclaredMethod(methodName, eventType);
return new SubscriberMethod(method, eventType, threadMode, priority, sticky);
} catch (NoSuchMethodException e) {
throw new EventBusException("Could not find subscriber method in " + subscriberClass +
". Maybe a missing ProGuard rule?", e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/
package org.greenrobot.eventbus.meta;

import org.greenrobot.eventbus.GeneratedSubscriberMethod;
import org.greenrobot.eventbus.SubscriberMethod;
import org.greenrobot.eventbus.ThreadMode;

/**
* Uses {@link SubscriberMethodInfo} objects to create {@link org.greenrobot.eventbus.SubscriberMethod} objects on demand.
Expand All @@ -35,9 +37,14 @@ public synchronized SubscriberMethod[] getSubscriberMethods() {
SubscriberMethod[] methods = new SubscriberMethod[length];
for (int i = 0; i < length; i++) {
SubscriberMethodInfo info = methodInfos[i];
methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
methods[i] = createSubscriberMethod(info.invoker, info.methodString, info.eventType, info.threadMode,
info.priority, info.sticky);
}
return methods;
}

protected SubscriberMethod createSubscriberMethod(SubscriberMethodInvoker invoker, String methodName, Class<?> eventType, ThreadMode threadMode,
int priority, boolean sticky) {
return new GeneratedSubscriberMethod(invoker, methodName, getSubscriberClass(), eventType, threadMode, priority, sticky);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,33 @@
import org.greenrobot.eventbus.ThreadMode;

public class SubscriberMethodInfo {
final String methodName;
final SubscriberMethodInvoker invoker;
final String methodString;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;

public SubscriberMethodInfo(String methodName, Class<?> eventType, ThreadMode threadMode,
public SubscriberMethodInfo(SubscriberMethodInvoker invoker,
String methodString,
Class<?> eventType, ThreadMode threadMode,
int priority, boolean sticky) {
this.methodName = methodName;
this.invoker = invoker;
this.methodString = methodString;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}

public SubscriberMethodInfo(String methodName, Class<?> eventType) {
this(methodName, eventType, ThreadMode.POSTING, 0, false);
public SubscriberMethodInfo(SubscriberMethodInvoker invoker,
String methodString,
Class<?> eventType) {
this(invoker, methodString, eventType, ThreadMode.POSTING, 0, false);
}

public SubscriberMethodInfo(String methodName, Class<?> eventType, ThreadMode threadMode) {
this(methodName, eventType, threadMode, 0, false);
public SubscriberMethodInfo(SubscriberMethodInvoker invoker, String methodString, Class<?> eventType, ThreadMode threadMode) {
this(invoker, methodString, eventType, threadMode, 0, false);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.greenrobot.eventbus.meta;


/**
* Interface for generated method invocation to avoid using reflection
*/
public interface SubscriberMethodInvoker {
void invoke(Object subscriber, Object event);
}
Loading