Данная статья является седьмой частью серии статей, предназначенных, по словам автора, для тех, кто не может разобраться с внедрением зависимостей и фреймворком Dagger 2, либо только собирается это сделать. Оригинал написан 30 декабря 2017 года. Перевод вольный.
Это седьмая статья цикла «Dagger 2 для начинающих Android разработчиков.». Если вы не читали предыдущие, то вам сюда.
Мы рассмотрели пример проекта и попытались избавиться от сильных связей с помощью внедрения зависимостей, используя Dagger 2 и аннотации.
Также изучили три новые аннотации.
Создание нескольких
В предыдущей статье мы создали зависимости уровня приложения. Но что, если требуются зависимости только для уровня
Лучшее решение заключается в создании отдельных модулей и компонентов для объектов, жизненные циклы которых различаются.
Для объяснения этого я не хочу добавлять новые объекты в ранее рассмотренный проект. Вместо этого рассмотрим нашу
Взгляните на ветку
Шаг 1. Создание области уровня
Для предстоящих изменений я создал отдельный пакет с названием
Создадим новую область (Scope) для
Шаг 2. Создание компонента для
Далее создадим отдельный компонент (Component) для
Необходимо позволить
В данном примере на уровне
Шаг 3. Создание модуля для
Теперь создадим модуль, который предоставит адаптер.
Заметка: Внедрение
Обратите внимание на аннотацию
Также необходимо сопоставить этот модуль с соответствующим компонентом. Воспользуемся атрибутом
Шаг 4. Создание класса
Этот класс, наследуемый от
Шаг 5. Доработка
Если вы соберете проект, то Dagger 2 сгенерирует для вас класс
Заметка: взгляните на метод
Мы создали достаточно поддерживаемый код. Сделали зависимости уровня
Если существует действительно много зависимостей, нужно ли нам постоянно писать выражения вроде того, что ниже?
Вы можете решить, что для вас это не важно, но и для этой проблемы есть решение.
Использование аннотации
Вместо того, чтобы указывать Dagger 2 что вам необходимы
Изменив классы так как ниже, мы сможем начать использовать аннотацию
Доработка
Удалим методы
Доработка
Как это работает? Когда Dagger 2 находит метод без возвращаемого значения (
То что нужно! Теперь код можно запускать.
Мы рассмотрели пример внедрения зависимостей на уровне
Спасибо, что потратили свое время на чтение и поддержку этой серии статей. Я надеюсь, вы получили некоторое представление о зависимостях и Dagger 2. Причина, по которой я написал эту серию статей заключается в том, что я улучшал свои знания Dagger 2 чтением большого количества статей в разных блогах, но получил ещё больше знаний, пока писал эти статьи для вас. Поэтому призываю всех читателей делиться своими знаниями любыми возможными путями. Я не эксперт в Dagger 2, я считаю себя лишь учеником.
Теперь вы можете продолжить изучать Dagger 2 дальше, эта серия статей должна была сформировать у вас достаточно понимание того, как он работает.
Это седьмая статья цикла «Dagger 2 для начинающих Android разработчиков.». Если вы не читали предыдущие, то вам сюда.
Серия статей
- Dagger 2 для начинающих Android разработчиков. Введение.
- Dagger 2 для начинающих Android разработчиков. Внедрение зависимостей. Часть 1 .
- Dagger 2 для начинающих Android разработчиков. Внедрение зависимостей. Часть 2.
- Dagger 2 для начинающих Android разработчиков. Dagger 2. Часть 1.
- Dagger 2 для начинающих Android разработчиков. Dagger 2. Часть 2.
- Dagger 2 для начинающих Android разработчиков. Dagger 2. Продвинутый уровень.
Часть 1. - Dagger 2 для начинающих Android разработчиков. Dagger 2. Продвинутый уровень.
Часть 2 (вы здесь).
Ранее в цикле статей
Мы рассмотрели пример проекта и попытались избавиться от сильных связей с помощью внедрения зависимостей, используя Dagger 2 и аннотации.
Также изучили три новые аннотации.
@Scope
для создания объектов в единственном экземпляре (singleton). @Named
для разделения методов, предоставляющих объекты одинакового типа. @Qualifier
как альтернативу @Named
.Создание нескольких Component
В предыдущей статье мы создали зависимости уровня приложения. Но что, если требуются зависимости только для уровня
Activity
? Activity
создается и уничтожается в своем жизненном цикле, а что происходит с зависимостями? Зависимости, созданные внутри Activity
уничтожаются вместе с Activity
.Лучшее решение заключается в создании отдельных модулей и компонентов для объектов, жизненные циклы которых различаются.
Для объяснения этого я не хочу добавлять новые объекты в ранее рассмотренный проект. Вместо этого рассмотрим нашу
MainActivity
как отдельный объект и создадим для него свой модуль и компонент.Взгляните на ветку
Dagger2Part2
.Шаг 1. Создание области уровня Activity
Для предстоящих изменений я создал отдельный пакет с названием
MainActivityFeature
. Создадим новую область (Scope) для
MainActivity
.@Scope
public @interface MainActivityScope {}
Шаг 2. Создание компонента для MainActivity
Далее создадим отдельный компонент (Component) для
MainActivity
и пометим его только что созданной аннотацией.@Component(dependencies = RandomUserComponent.class)
@MainActivityScope
public interface MainActivityComponent {
RandomUserAdapter getRandomUserAdapter();
RandomUsersApi getRandomUserService();
}
Необходимо позволить
MainActivityComponent
ссылаться на RandomUserComponent
, для чего используется атрибут dependencies
. Другими словами, этот атрибут говорит Dagger 2 обращаться к RandomUserComponent
, если требуются дополнительные зависимости.В данном примере на уровне
Activity
нам потребуются адаптер для API и объект для создания вызовов к RandomUsersAPI
. Следовательно, реализуем методы getRandomUserAdapter()
и getRandomUserService()
.Шаг 3. Создание модуля для MainActivity
Теперь создадим модуль, который предоставит адаптер.
@Module
public class MainActivityModule {
private final MainActivity mainActivity;
public MainActivityModule(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
@Provides
@MainActivityScope
public RandomUserAdapter randomUserAdapter(Picasso picasso){
return new RandomUserAdapter(mainActivity, picasso);
}
}
Заметка: Внедрение
MainActivity
через адаптер не обязательно, я сделал это для примера. Если нужен контекст для Picasso
, то можно использовать holder.imageView.getContext()
.Обратите внимание на аннотацию
@MainActivityScope
, которая добавлена к методу randomUserAdapter()
чтобы ограничить область использования зависимости уровнем Activity
.Также необходимо сопоставить этот модуль с соответствующим компонентом. Воспользуемся атрибутом
modules
.@Component(modules = MainActivityModule.class, dependencies = RandomUserComponent.class)
@MainActivityScope
public interface MainActivityComponent {
RandomUserAdapter getRandomUserAdapter();
RandomUsersApi getRandomUserService();
}
Шаг 4. Создание класса Application
public class RandomUserApplication extends Application {
// добавьте имя этого класса в манифест
private RandomUserComponent randomUserApplicationComponent;
public static RandomUserApplication get(Activity activity){
return (RandomUserApplication) activity.getApplication();
}
@Override
public void onCreate() {
super.onCreate();
Timber.plant(new Timber.DebugTree());
randomUserApplicationComponent = DaggerRandomUserComponent.builder()
.contextModule(new ContextModule(this))
.build();
}
public RandomUserComponent getRandomUserApplicationComponent(){
return randomUserApplicationComponent;
}
Этот класс, наследуемый от
Application
, содержит все зависимости уровня приложения — RandomUserApplicationComponent
.Шаг 5. Доработка MainActivity
Если вы соберете проект, то Dagger 2 сгенерирует для вас класс
DaggerMainActivityComponent
. Для использования зависимостей уровня Activity
нам понадобится получить некоторые зависимости уровня приложения.public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
....
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityModule(new MainActivityModule(this))
.randomUserComponent(RandomUserApplication.get(this).getRandomUserApplicationComponent())
.build();
randomUsersApi = mainActivityComponent.getRandomUserService();
mAdapter = mainActivityComponent.getRandomUserAdapter();
....
}
}
Заметка: взгляните на метод
afterActivityLevelComponent()
в ветке с проектом.Шаг 6. Поздравьте себя
Мы создали достаточно поддерживаемый код. Сделали зависимости уровня
Activity
. Поздравьте себя.А что, если в компоненте 50 зависимостей?
Если существует действительно много зависимостей, нужно ли нам постоянно писать выражения вроде того, что ниже?
randomUserApi = mainActivityComponent.getRandomUserService();
mAdapter = mainActivityComponent.getRandomUserAdapter();
…
Вы можете решить, что для вас это не важно, но и для этой проблемы есть решение.
Использование аннотации @Inject
Вместо того, чтобы указывать Dagger 2 что вам необходимы
RandomUserService
и RandomUserAdapter
, пусть Dagger 2 обрабатывает поле, которые мы пометим аннотацией @Inject
.Изменив классы так как ниже, мы сможем начать использовать аннотацию
@Inject
в кратчайшие сроки. Полный пример вы можете просмотреть в следующей ветке.Доработка MainActivityComponent
Удалим методы
getRandomUserService()
и getRandomUserAdapter()
и добавим метод для внедрения MainActivity
.@Component(modules = MainActivityModule.class, dependencies = RandomUserComponent.class)
@MainActivityScope
public interface MainActivityComponent {
void injectMainActivity(MainActivity mainActivity);
}
Доработка MainActivity
public class MainActivity extends AppCompatActivity {
....
@Inject
RandomUsersApi randomUsersApi;
@Inject
RandomUserAdapter mAdapter;
....
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
.....
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityModule(new MainActivityModule(this))
.randomUserComponent(RandomUserApplication.get(this).getRandomUserApplicationComponent())
.build();
mainActivityComponent.injectMainActivity(this);
....
}
}
Как это работает? Когда Dagger 2 находит метод без возвращаемого значения (
void
) он понимает, что должно быть что-то, что ему нужно в классе, то есть он будет инициализировать в классе поля, помеченные аннотацией @Inject
.То что нужно! Теперь код можно запускать.
GIF
Резюме
Мы рассмотрели пример внедрения зависимостей на уровне
Activity
. Также увидели пример использования аннотации @Inject
.В заключение
Спасибо, что потратили свое время на чтение и поддержку этой серии статей. Я надеюсь, вы получили некоторое представление о зависимостях и Dagger 2. Причина, по которой я написал эту серию статей заключается в том, что я улучшал свои знания Dagger 2 чтением большого количества статей в разных блогах, но получил ещё больше знаний, пока писал эти статьи для вас. Поэтому призываю всех читателей делиться своими знаниями любыми возможными путями. Я не эксперт в Dagger 2, я считаю себя лишь учеником.
Теперь вы можете продолжить изучать Dagger 2 дальше, эта серия статей должна была сформировать у вас достаточно понимание того, как он работает.
Ссылки на другие ресурсы (на английском)
- https://blog.mindorks.com/introduction-to-dagger-2-using-dependency-injection-in-android-part-1-223289c2a01b
- https://blog.mindorks.com/introduction-to-dagger-2-using-dependency-injection-in-android-part-2-b55857911bcd
- https://blog.mindorks.com/the-new-dagger-2-android-injector-cbe7d55afa6a
- https://blog.mindorks.com/android-dagger2-critical-things-to-know-before-you-implement-275663aecc3e
- https://blog.mindorks.com/a-complete-guide-to-learn-dagger-2-b4c7a570d99c
- http://www.vogella.com/tutorials/Dagger/article.html
- https://medium.com/@iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe
popalay1
Статья морально устарела
tehreh1uneh Автор
Хотелось бы услышать аргументацию.
popalay1
Например, для создание компонентов для activity/fragment стоит использовать Dagger Android, что сильно уменьшит количество кода