Я использую Dagger2 для DI в своем приложении для Android. Я обнаружил, что мне нужно написать метод инъекции для каждого класса, который использует поле @Inject. Есть ли способ, которым я могу просто ввести родительский класс, чтобы мне не приходилось вызывать инъекцию для каждого подкласса? Возьмем, к примеру, Activity. У меня есть BaseActivity
, от которого исходит каждое действие. Есть ли способ, которым я могу просто создать метод инъекции в компоненте для BaseActivity и просто вызвать инъекцию в onCreate BaseActivity, а поля @inject во вспомогательных действиях будут вводиться автоматически?
Могу ли я просто ввести суперкласс при использовании dagger2 для инъекции зависимостей?
Ответы (3)
Это невозможно сделать прямо сейчас. Объяснение Грегори Кика (здесь):
Вот как работают методы внедрения членов:
- Вы можете создать метод внедрения членов для любого типа, имеющего
@Inject
в любом месте своей иерархии классов. Если этого не произойдет, вы получите сообщение об ошибке.- Будут введены все
@Inject
ed члены во всей иерархии типов: тип аргумента и все супертипы.- Никакие члены не будут
@Inject
ed для подтипов типа аргумента.
Эта проблема обсуждалась здесь и здесь, следите за обновлениями. Но вряд ли это скоро изменится, потому что Dagger 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, который получает общий компонент из приложения и вызывает абстрактный метод 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
.
Вы можете сделать небольшой взлом, используя отражение:
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> { *; }
в свои правила, чтобы сохранить методы инъекции такими, как они есть в компоненте.