This interface represents something that an EventHandler can call whenever it is posted an Event.
+ * + * @author Guillermo Gutierrez + */ +public interface CallbackThis method will be called whenever an Event is posted to the EventHandler that has an implementation of this + * interface.
+ * + * @param event event object that has been posted + * @throws InvocationTargetException + */ + void call(T event) throws InvocationTargetException; +} diff --git a/library/src/main/java/com/squareup/otto/EventHandler.java b/library/src/main/java/com/squareup/otto/EventHandler.java index 843a31b..8547a33 100644 --- a/library/src/main/java/com/squareup/otto/EventHandler.java +++ b/library/src/main/java/com/squareup/otto/EventHandler.java @@ -25,7 +25,7 @@ * *This class only verifies the suitability of the method and event type if something fails. Callers are expected t * verify their uses of this class. - * + *
*Two EventHandlers are equivalent when they refer to the same method on the same object (not class). This * property is used to ensure that no handler method is registered more than once. * @@ -33,10 +33,8 @@ */ class EventHandler { - /** Object sporting the handler method. */ - private final Object target; - /** Handler method. */ - private final Method method; + /** Handler Callback. */ + private final Callback callback; /** Object hash code. */ private final int hashCode; /** Should this handler receive events? */ @@ -50,14 +48,23 @@ class EventHandler { throw new NullPointerException("EventHandler method cannot be null."); } - this.target = target; - this.method = method; - method.setAccessible(true); + // Compute hash code eagerly since we know it will be used frequently and we cannot estimate the runtime of the + // target's hashCode call. + final int prime = 31; + this.callback = new MethodInvokingCallback(target, method); + this.hashCode = (prime + callback.hashCode()) * prime + target.hashCode(); + } + + EventHandler(final Callback callback) { + if (callback == null) { + throw new NullPointerException("EventHandler callback cannot be null."); + } // Compute hash code eagerly since we know it will be used frequently and we cannot estimate the runtime of the // target's hashCode call. final int prime = 31; - hashCode = (prime + method.hashCode()) * prime + target.hashCode(); + this.callback = callback; + this.hashCode = (prime + callback.hashCode()) * prime; } public boolean isValid() { @@ -74,31 +81,24 @@ public void invalidate() { } /** - * Invokes the wrapped handler method to handle {@code event}. + * Invokes the wrapped handler callback to handle {@code event}. * - * @param event event to handle - * @throws java.lang.IllegalStateException if previously invalidated. - * @throws java.lang.reflect.InvocationTargetException if the wrapped method throws any {@link Throwable} that is not - * an {@link Error} ({@code Error}s are propagated as-is). + * @param event event to handle + * @throws java.lang.reflect.InvocationTargetException if the wrapped callback throws any + * {@link Throwable} that is not + * an {@link Error} ({@code Error}s + * are propagated as-is). */ + @SuppressWarnings("unchecked") public void handleEvent(Object event) throws InvocationTargetException { if (!valid) { throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events."); } - try { - method.invoke(target, event); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } catch (InvocationTargetException e) { - if (e.getCause() instanceof Error) { - throw (Error) e.getCause(); - } - throw e; - } + callback.call(event); } @Override public String toString() { - return "[EventHandler " + method + "]"; + return "[EventHandler " + callback.toString() + "]"; } @Override public int hashCode() { @@ -120,7 +120,11 @@ public void handleEvent(Object event) throws InvocationTargetException { final EventHandler other = (EventHandler) obj; - return method.equals(other.method) && target == other.target; + return callback.equals(other.callback); } + + public boolean hasCallback(Callback callback) { + return this.callback.equals(callback); + } } diff --git a/library/src/main/java/com/squareup/otto/MethodInvokingCallback.java b/library/src/main/java/com/squareup/otto/MethodInvokingCallback.java new file mode 100644 index 0000000..5554cfb --- /dev/null +++ b/library/src/main/java/com/squareup/otto/MethodInvokingCallback.java @@ -0,0 +1,85 @@ +package com.squareup.otto; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + *
Extracted from EventHandler's original implementation
+ *Wraps a Method in a Callback interface in order to be called whenever the EventHandler is posted an Event
+ *This class only verifies the suitability of the method and event type if something fails. Callers are expected to + * verify their uses of this class.
+ *Two MethodInvokingCallback are equivalent when they refer to the same method on the same object (not class). + * This property is used to ensure that no handler method is registered more than once.
+ * + * @author Guillermo Gutierrez + */ +public class MethodInvokingCallback implements Callback { + private final Object target; + private final Method method; + private final int hashCode; + + public MethodInvokingCallback(Object target, Method method) { + method.setAccessible(true); + this.target = target; + this.method = method; + // Compute hash code eagerly since we know it will be used frequently and we cannot estimate the runtime of the + // target's hashCode call. + final int prime = 31; + hashCode = (prime + method.hashCode()) * prime + target.hashCode(); + } + + /** + *This method will be called whenever an Event is posted to the EventHandler that has an implementation of this + * interface.
+ * + * @param event event to handle + * @throws java.lang.reflect.InvocationTargetException if the wrapped method throws any {@link Throwable} that is not + * an {@link Error} ({@code Error}s are propagated as-is). + */ + @Override + public void call(Object event) throws InvocationTargetException { + try { + method.invoke(target, event); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Error) { + throw (Error) e.getCause(); + } + throw e; + } + } + + public Method getMethod() { + return method; + } + + @Override + public String toString() { + return method.toString(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + final MethodInvokingCallback other = (MethodInvokingCallback) obj; + + return method.equals(other.getMethod()) && target == other.target; + } +} diff --git a/library/src/test/java/com/squareup/otto/BusTest.java b/library/src/test/java/com/squareup/otto/BusTest.java index 5f42e9b..4ed98da 100644 --- a/library/src/test/java/com/squareup/otto/BusTest.java +++ b/library/src/test/java/com/squareup/otto/BusTest.java @@ -19,6 +19,7 @@ import org.junit.Before; import org.junit.Test; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -305,6 +306,66 @@ class VoidProducer { expectedEvents, catcher2.getEvents()); } + @Test public void unregisterCallback() { + final StringCatcher catcher1 = new StringCatcher(); + final StringCatcher catcher2 = new StringCatcher(); + Callback catcher1Callback = new Callback