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

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

3 ответов


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

вот как работают методы инъекций членов:

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

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


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

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

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) Наконец, вы должны фактически ввести each 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);
    }
}

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

UPDATE: инъекция объектов @ActivityScope во фрагменты

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

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

вам нужно будет изменить DaggerActivity. The 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, the Fragments расширения FragmentDagger есть только один способ реализации:

public abstract void injectFragment(ActivityComponent component);

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


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

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

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

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

осторожно! Если вы используете Proguard, вам нужно добавить next -keep class <ComponentClass> { *; } к вашим правилам держать впрыснуть методы как в компоненте