Могу ли я просто ввести суперкласс при использовании dagger2 для инъекции зависимостей?

Я использую Dagger2 для DI в своем приложении для Android. Я обнаружил, что мне нужно написать метод инъекции для каждого класса, который использует поле @Inject. Есть ли способ, которым я могу просто ввести родительский класс, чтобы мне не приходилось вызывать инъекцию для каждого подкласса? Возьмем, к примеру, Activity. У меня есть BaseActivity, от которого исходит каждое действие. Есть ли способ, которым я могу просто создать метод инъекции в компоненте для BaseActivity и просто вызвать инъекцию в onCreate BaseActivity, а поля @inject во вспомогательных действиях будут вводиться автоматически?


person Chris.Zou    schedule 28.03.2015    source источник
comment
Не могли бы вы добавить пример кода, чтобы показать, что вы имеете в виду?   -  person nhaarman    schedule 28.03.2015
comment
просто вопрос (если я правильно понял ваш вопрос), почему бы вам не определить поле ввода в базовой активности и не использовать его в дочерней деятельности. Любой такой случай, когда вы хотите, чтобы ваши поля ввода во вспомогательных действиях вводились автоматически   -  person JSONParser    schedule 10.10.2019


Ответы (3)


Это невозможно сделать прямо сейчас. Объяснение Грегори Кика (здесь):

Вот как работают методы внедрения членов:

  1. Вы можете создать метод внедрения членов для любого типа, имеющего @Inject в любом месте своей иерархии классов. Если этого не произойдет, вы получите сообщение об ошибке.
  2. Будут введены все @Injected члены во всей иерархии типов: тип аргумента и все супертипы.
  3. Никакие члены не будут @Injected для подтипов типа аргумента.

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

person Kirill Boyarshinov    schedule 29.03.2015
comment
Похоже, что они приняли такое решение неспроста. Но все же жаль, что они этого не поддерживают, потому что это немного нелогично ИМХО. Тем не менее, спасибо за ответ! - person Chris.Zou; 05.04.2015
comment
@ Chris.Zou Для поддержки внедрения подкласса вам нужно будет выполнять рефлексию во время выполнения. Команда Dagger 2 на раннем этапе решила, что они не хотят делать что-то во время выполнения, поскольку он медленнее, и вы не узнаете об ошибках, пока не запустите приложение. - person vaughandroid; 22.06.2015

Я столкнулся с такой же ситуацией. Один из способов немного облегчить внедрение из общего компонента во все действия:

1) Расширьте класс Application, чтобы иметь возможность создавать общий компонент и сохранять на него ссылку.

public class ApplicationDagger extends Application {

    private ApplicationComponent component;

    @Override
    public void onCreate(){
        super.onCreate();
        component = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();
    }

    public ApplicationComponent getComponent(){
            return component;
    }
}

2) Создайте абстрактный DaggerActivity, который получает общий компонент из приложения и вызывает абстрактный метод injectActivity, передавая компонент в качестве аргумента. Нравится:

public abstract class DaggerActivity extends Activity {

    @Override
    public void onCreate(Bundle saved){
        super.onCreate(saved);
        ApplicationComponent component = ((ApplicationDagger) getApplication()).getComponent();
        injectActivity(component);
    }

    public abstract void injectActivity(ApplicationComponent component);
}

3) Наконец, вы должны ввести каждое Activity расширение DaggerActivity. Но сейчас это можно сделать с меньшими усилиями, так как вам нужно реализовать метод abstract, иначе вы получите ошибки компиляции. Вот так:

public class FirstActivity extends DaggerActivity {

    @Inject
    ClassToInject object;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //initialize your Activity
    }

    @Override
    public void injectActivity(ApplicationComponent component) {
        component.inject(this);
    }
}

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

ОБНОВЛЕНИЕ: внедрение объектов @ActivityScope во фрагменты

В какой-то момент мне понадобилось использовать пользовательские области привязать объекты к Activity жизненному циклу. Я решил расширить этот пост, так как он может помочь некоторым людям.

Допустим, у вас есть @Module класс ActivityModule и интерфейс @Subcomponent ActivityComponent.

Вам нужно будет изменить DaggerActivity. Для Activities расширения DaggerActivity потребуется реализовать новый метод (изменение подписи).

public abstract class ActivityDagger extends AppCompatActivity {

    ActivityComponent component;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        component = ((ApplicationDagger) getApplication()).getComponent().plus(new ActivityModule(this));
        injectActivity(component);
        super.onCreate(savedInstanceState);
    }

    ActivityComponent getComponent() {
        return component;
    }

    public abstract void injectActivity(ActivityComponent component);
}

Затем класс FragmentDagger, расширяющий Fragment, может быть создан следующим образом:

public abstract class FragmentDagger extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityDagger activityDagger = (ActivityDagger) getActivity();
        ActivityComponent component = activityDagger.getComponent();
        injectFragment(component);
    }

    public abstract void injectFragment(ActivityComponent component);

}

Что касается Activities, Fragments расширение FragmentDagger имеет только один метод для реализации:

public abstract void injectFragment(ActivityComponent component);

У вас должна быть возможность повторно использовать Fragments где угодно. Обратите внимание, что метод super.onCreated() в ActivityDagger должен вызываться после создания экземпляра компонента. В противном случае вы получите NullPointerException, когда Activity состояние воссоздается, потому что будет вызван метод super.onCreate() из Fragment.

person Gordak    schedule 25.06.2015

Вы можете сделать небольшой взлом, используя отражение:

public class UiInjector {

    private static final String METHOD_NAME = "inject";

    private final UIComponent component;

    public UiInjector(final UIComponent component) {
        this.component = component;
    }

    public void inject(final Object subject) {
        try {
            component.getClass()
                    .getMethod(METHOD_NAME, subject.getClass())
                    .invoke(component, subject);
        } catch (final NoSuchMethodException exception) {
            throwNoInjectMethodForType(component, subject.getClass());
        } catch (final Exception exception) {
            throwUnknownInjectionError(exception);
        }
    }

    private void throwNoInjectMethodForType(final Object component, final Class subjectType) {
        throw new RuntimeException(component.getClass().getSimpleName() +
                " doesn't have inject method with parameter type : " + subjectType);
    }

    private void throwUnknownInjectionError(final Exception cause) {
        throw new RuntimeException("Unknown injection error", cause);
    }
}

В этом случае вам все равно нужно написать метод инъекции в компоненте, но вам не нужен метод инъекции в каждом действии, фрагменте, представлении и т. Д.

Почему это работает? когда мы используем getClass() для инъекции, объект получит класс-потомок, а не базу.

Осторожность! В случае, если вы используете Proguard, вам нужно добавить следующий -keep class <ComponentClass> { *; } в свои правила, чтобы сохранить методы инъекции такими, как они есть в компоненте.

person Andriy Pokynboroda    schedule 06.06.2017
comment
dagger должен быть проверен во время компиляции, так что это нарушит эту цель ... - person AA_PV; 09.06.2020