Что происходит с использованием greenrobot EventBus, если подписывается и базовый класс, и производный класс? - PullRequest
0 голосов
/ 31 марта 2019

Я использую Greenrobot EventBus.

Если у меня есть базовый класс B и производный класс D, и оба подписаны на событие E в функции f:

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. Только D.f() называется
  3. D.f() вызывается дважды (один раз для каждой регистрации)

1 Ответ

0 голосов
/ 31 марта 2019

Действительно для версии 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 переопределяет метод void f(E e) в B.

Ниже объясняется, почему метод вызывается один рази только один раз .

В частности, когда вы регистрируете подписчика, метод 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).Это возвращает ложь - в результате чего он не будет добавлен к findState.

Это отклонение связано с тем, что SubscribeMethodFinder::checkAddWithMethodSignature запрещает делать методы кандидатами на подписку, если они переопределены дочерним элементом вcurrent 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");
    }
}

Тогда *Метод 1066 * даст:

IN D
IN B

Я предполагаю, что порядок определяется в порядке следования от потомка к родителю (т. Е. D extends B, поэтому D идет первым).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...