使用 greenrobot EventBus,如果基础 class 和派生 class 都订阅,会发生什么?

Using greenrobot EventBus, if both a base class and a derived class subscribe, what happens?

我正在使用 greenrobot EventBus。

如果我有一个基础 class B 和一个派生的 class D,并且都在函数 f:

中订阅了一个事件 E
class E {}

class B {
    @Subscribe
    public f(E e) {}
}

class D extends B {
    @Subscribe
    public f(E e) {}
}

发送事件时会发生什么,并且存在类型 D 的对象?

  1. B.f()D.f() 都在对象
  2. 上被调用
  3. 只有D.f()被调用
  4. 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 object 用于捕获查找。

随后,findState.checkAdd(method, eventType)首先是SubscriberMethodFinder::findUsingReflectionInSingleClass中的运行D::f(我们正在注册一个D,它在层次结构的底部),所以它 returns true -- 这会导致为订阅通知添加 D::f 方法。

然后它沿着层次结构向上移动。

因此,第二次调用(使用 B::f)。这个 returns false - 导致它不被添加到 findState.

拒绝是因为SubscribeMethodFinder::checkAddWithMethodSignature禁止在当前FindState中被child覆盖的方法进行订阅:

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;
    }
}

下面解释了为什么只调用了child class'覆盖的实现.

由于我们无法选择调用层次结构方法中的哪种类型这一事实进一步加强了这一点 - 如果我们调用被覆盖的方法,将调用 child-est 版本。静态和 运行 时间类型无关紧要。

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

我猜顺序是按 child-to-parent 顺序确定的(即 D extends B 所以 D 排在第一位)。