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

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

Ответ 1

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

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

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

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

Ответ 2

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

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, которая получает общий компонент из Application и вызывает абстрактный метод 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. Я решил расширить этот пост, так как он может помочь некоторым людям.

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

Вам нужно будет изменить 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() метод super.onCreate() Fragment.

Ответ 3

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

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