#android #dependency-injection #dagger-2
#Android #внедрение зависимостей #dagger-2
Вопрос:
Android Studio 2.2.2
У меня есть NewsListModelImp
класс, который является моделью в MVP.
Я хочу внедрить свой модифицированный сервис в модель. Однако, поскольку NewsListModelImp
не содержит никаких ссылок на контекст или действие, которые я не могу вызвать getApplication()
. Это то, что вы бы сделали, если бы находились в activity или фрагменте. Я не хочу передавать какой-либо контекст или activity в конструкторе NewsListModeImp
, поскольку это должно исходить от presenter, и я хочу избежать любых материалов Android там.
public class NewsListModelImp implements NewsListModelContract {
@Inject
NYTimesSearchService mNYTimesSearchService;
public NewsListModelImp() {
((NYTimesSearchApplication)getApplication()).getAppComponent().inject(this);
}
}
Мой класс приложения
public class NYTimesSearchApplication extends Application {
private AppComponent mAppComponent;
public void onCreate() {
super.onCreate();
/* Setup dependency injection */
createAppComponent();
}
private void createAppComponent() {
mAppComponent = DaggerAppComponent
.builder()
.retrofitModule(new RetrofitModule())
.build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
Мой модуль provides
@Module
public class RetrofitModule {
private Retrofit retrofit() {
return new Retrofit
.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Provides
@Singleton
public NYTimesSearchService providesNYTimesSearch() {
return retrofit().create(NYTimesSearchService.class);
}
}
Мой Appcomponent
@Singleton
@Component(modules = {RetrofitModule.class})
public interface AppComponent {
void inject(NewsListModelImp target);
}
Большое спасибо за любые предложения,
Ответ №1:
Dagger-2
работает постоянно. Итак, если внедрен внутренний Activity
(или Fragment
@Inject
) объект, и его конструктор должным образом аннотирован с помощью ,,аннотации,, параметры конструктора также будут введены.
Предположим, что внутри приложения вы хотели бы внедрить:
@Inject NyTimesPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((NYTimesSearchApplication) getApplication()).getAppComponent().inject(this);
}
Конструктор NyTimesPresenter
должен быть снабжен аннотацией @Inject
:
public class NyTimesPresenter {
NewsListModelImp newsListModel;
@Inject
public NyTimesPresenter(NewsListModelImp newsListModel) {
this.newsListModel = newsListModel;
}
}
NewsListModelImp
конструктор также должен быть снабжен аннотацией @Inject
:
public class NewsListModelImp implements NewsListModelContract {
NYTimesSearchService mNYTimesSearchService;
@Inject
public NewsListModelImp(NYTimesSearchService nYTimesSearchService) {
this.mNYTimesSearchService = nYTimesSearchService;
}
}
Тогда все будет внедрено должным образом.
Почему параметры должны передаваться классу в качестве параметров конструкторов? Такой шаблон проектирования соответствует SOLID principles
. Зависимости объектов внедряются в объекты, а не создаются внутри них, и такой код легко тестируется (в тестах зависимости могут быть заменены ie. от Mock
‘s)
ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ:
Можно внедрять объекты, реализующие определенные интерфейсы. Такая техника описана здесь. В вашем случае NyTimesPresenter
может иметь NewsListModelContract
, поскольку это зависимость вместо NewsListModelImp
. Для этого добавьте другой модуль в свой AppComponent
:
@Singleton
@Component(
modules = {
RetrofitModule.class,
AppModule.class
})
public interface AppComponent {
AppComponent
метод для предоставления интерфейса реализации конкретного класса должен выглядеть следующим образом:
@Singleton
@Module
public abstract class AppModule {
@Binds
public abstract NewsListModelContract provideNewsListModelContract(NewsListModelImp newsListModelImp);
}
Реализация NyTimesPresenter
должна измениться (просто заменить конкретный класс интерфейсом, который он реализует):
public class NyTimesPresenter {
NewsListModelContract newsListModel;
@Inject
public NyTimesPresenter(NewsListModelContract newsListModel) {
this.newsListModel = newsListModel;
}
}
Комментарии:
1. Именно то, что я хотел.. Спасибо, Заг