Кинжал 2 на Android. Различные способы хранения и доступа к компоненту @Singleton

Это N-й вопрос о том, как хранить компоненты Dagger 2 с компонентами @Singleton, срок службы которых должен равняться продолжительности жизни приложения.

В приложениях Android, использующих Dagger 2, обычно есть как минимум один компонент, который является @Singleton, и должен длиться всю жизнь приложения: из-за этих требований он обычно инициализируется и хранится внутри пользовательского класса Application.

Поскольку экземпляр этого компонента должен быть доступен во всех частях нашего приложения, я видел такой код:

1. Храните компонент в общедоступной статической переменной внутри класса приложения.

public class App extends Application { public static AppComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)).build(); } } 

Таким образом, он может быть доступен в любом месте:

 App.appComponent.inject(this); 

2. Храните компонент в частной переменной внутри экземпляра приложения и создайте для него статический аксессор.

 public class App extends Application { private static AppComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)).build(); } public static AppComponent getAppComponent() { return appComponent; } } 

Таким образом, он может быть доступен в любом месте:

 App.getAppComponent().inject(this); 

3. Храните компонент в частной переменной внутри экземпляра приложения и создайте для него нестационарный аксессор.

 public class App extends Application { private AppComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)).build(); } public AppComponent getAppComponent() { return appComponent; } } 

Таким образом, доступ к нему возможен только из экземпляров класса, которые содержат ссылку на контекст:

 // From within an Activity. ((App) getApplication()).getAppComponent().inject(this); // From within a Fragment. ((App) getActivity().getApplication()).getAppComponent().inject(this); // From within any other class which holds a reference to a Context. ((App) context.getApplicationContext()).getAppComponent().inject(this); 

Последнее позволяет сделать ссылку на контекст любым классом, желающим получить доступ к компоненту (даже если этот контекст не нужен этому классу для любых других целей).

ИМХО, которому приходится «вручную вводить» экземпляр контекста только для доступа к самому инжектору, звучит немного интуитивно понятным.

С другой стороны, многие советуют использовать статические переменные, но: почему? Если объект должен оставаться в памяти для времени жизни приложения (что означает для всего времени жизни экземпляра JVM), в чем проблема, если он хранится в статической переменной?

Другие говорят, что статические вещи не могут быть издевались над тестированием, и это правда, хотя я не уверен, что полностью понимаю это, потому что это шаблон DI, который позволяет легко насмехаться / тестировать, а не сам инжектор, так почему мы хотим Высмеивать сам инжектор?

Каковы плюсы и минусы этих альтернатив? Существуют ли другие альтернативы, кроме уже упомянутых здесь?

Solutions Collecting From Web of "Кинжал 2 на Android. Различные способы хранения и доступа к компоненту @Singleton"

С 1 и 2 вы используете статические ссылки. Это хороший вопрос о том, почему их избегать

Почему статические переменные считаются злыми?

Таким образом, единственный вариант – 3-й. Это то, что я использую в своих проектах. О том, следует ли передавать контекст в качестве аргумента или нет, зависит от архитектуры вашего проекта и того, как вы создали зависимости от кинжала. Лично у меня нет этой проблемы, потому что я только впрыскиваю в «Действия / Фрагменты». Можете ли вы привести мне пример, где вам нужно передать контекст для инъекций зависимостей?

Я использую метод # 2. Основная проблема с методом # 1 заключается в том, что вы подвергаете изменяемое поле. Если ваш модуль не требует создания Context , вы можете сделать поле final . Но, по сути, я по-прежнему предпочитаю не раскрывать поля.

Обычно вы должны избегать глобального состояния, особенно в Android, из-за сложных, а иногда и неинтуитивных жизненных циклов компонентов и самой виртуальной машины. Но Application является исключением из этого правила. Точно один экземпляр этого существует для каждой виртуальной машины, и его onCreate() вызывается ровно один раз, прежде чем будет создан какой-либо другой компонент. Это делает его приемлемым местом для создания и хранения статического синглтона.