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