Как внедрить в класс Java, у которого нет никакой активности или фрагмента, используя dagger2

#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. Именно то, что я хотел.. Спасибо, Заг