我正在使用greenrobot EventBus。
如果我有一个基类B和一个派生类D,并且都在函数f中订阅了一个事件E:
class E {}
class B {
@Subscribe
public f(E e) {}
}
class D extends B {
@Subscribe
public f(E e) {}
}
发送事件时会发生什么,并且存在D类型的对象?
B.f()
和D.f()
都被称为对象D.f()
D.f()
被召唤两次(每次登记一次)适用于3.1.1版
假设您刚刚注册了D
的实例:
public class Scratch4 {
public static void main(String args[]) {
EventBus.getDefault()
.register(new D());
EventBus.getDefault()
.post(new E());
}
}
和:
public class B {
@Subscribe
public void f(E e) {
System.out.println("IN B");
}
}
public class D extends B {
@Subscribe
public void f(E e) {
System.out.println("IN D");
}
}
然后main()
方法产生:
IN D
这是因为D覆盖了B中的方法void f(E e)
。
下面解释了为什么该方法只被调用一次。
具体来说,当您注册订阅者时,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);
}
}
}
只注册一种方法(D::f
)。
在引擎盖下,FindState
对象用于捕获查找。
随后,findState.checkAdd(method, eventType)
首先在SubscriberMethodFinder::findUsingReflectionInSingleClass
中运行D::f
(我们正在注册一个D,它位于层次结构的底部),所以它返回true - 这导致D::f
方法被添加用于订阅通知。
然后它走向层次结构。
所以,它第二次被调用(使用B::f
)。返回false - 导致它不被添加到findState
。
这种拒绝是因为如果被当前的SubscribeMethodFinder::checkAddWithMethodSignature
中的孩子覆盖,FindState
禁止制作候选方法。
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
下面解释了为什么只调用子类的覆盖实现。
由于我们无法选择层次结构方法中的哪种类型来调用,因此进一步强制执行 - 如果我们调用被覆盖的方法,则将调用最低版本的版本。静态和运行时类型无关紧要。
public class Scratch4 {
public static void main(String args[]) throws Exception {
B b = new D();
Class<B> bClazz = B.class;
Method bClazzMethod = bClazz.getMethod("f", E.class);
bClazzMethod.invoke(b, new E());
}
}
产量:
IN D
如果你要制作B
:
public class B {
@Subscribe
public void somethingThatIsNotF(E e) {
System.out.println("IN B");
}
}
然后main()
方法将产生:
IN D
IN B
我猜这个顺序是按照孩子对父母的顺序决定的(即D extends B
所以D
首先出现)。