Паттерн dependency injection android

Содержание
  1. Чем отличается Dependency injection от Service Locator ?
  2. Объясните мне, что такое внедрение зависимости (Dependency injection) ?
  3. А теперь расскажите что такое ServiceLocator
  4. Недостатки ServiceLocator
  5. Ок. Ясно. Так что в итоге использовать?
  6. Dependency injection
  7. От переводчика
  8. Серия включает в себя следующие статьи
  9. Внедрение зависимостей
  10. Объяснение внедрения зависимостей
  11. Цепное внедрение зависимостей
  12. Dagger 2. Лечим зависимости по методике Google
  13. Dependency Injection
  14. Что, зачем и когда это нужно
  15. Создание классов порождает…
  16. Возможность создавать объекты…
  17. И… зависимости
  18. Dagger and JSR-330 Standart
  19. Аннотация Inject
  20. Немного еще НЕ истории
  21. Инверсия управления (англ. Inversion of Control, IoC)
  22. Reflection vs Compile time
  23. JSR-330
  24. JSR-330 a.k.a. javax.inject
  25. Итак, пришло время добавить в наш код немного магии…
  26. Обратите внимание на конструктор по умолчанию
  27. Конструктор с параметрами — хорошее место для модификаций
  28. И все же — как это заставить работать?
  29. Структура инжекции Dagger2.0
  30. Компоненты и Модули
  31. Модуль — коллекция генераторов
  32. Что такое синглетон (англ. Singleton)?
  33. @Component
  34. Инициализация компонента generated code used
  35. Inject This! 🙂
  36. Puttin’ magic will work only after injection… 🙂
  37. Custom Scopes и эффективное управление памятью
  38. Жизненный цикл объектов
  39. И еще раз та же матрешка Компонент имеет область жизни (scope)
  40. This mysterious ‘plus’.
  41. Объявление субкомпонента
  42. Но ведь там был модуль!
  43. Добавление субкомпонента к корню дерева зависимостей
  44. Аннотация Scope
  45. @ActivityScope
  46. @UserScope

Чем отличается Dependency injection от Service Locator ?

Продолжаю тему паттернов проектирования и в этом посте мы рассмотрим чем отличаются 2 озвученных в заголовке паттерна и для чего они нужны на примерах.

Объясните мне, что такое внедрение зависимости (Dependency injection) ?

Верите или нет, но многие Android — разработчики, использовали DI с самого первого приложения, даже не зная о таких вещах как Dagger, Koin или другие фреймворки, облегчающие внедрение зависимостей. Как такое возможно? Давайте рассмотрим азы внедрения зависимости на примере.

Посмотрите внимательно на код класса CarA , и картинку внизу, наглядно показывающую такой подход:

  • CarA и Engine тесно связаны — экземпляр класса CarA использует экземпляр класса Engine , здесь невозможно использовать наследников данного класса или заменить его другим типом. Если класс CarA будет самостоятельно создавать экземпляр класса Engine , то придётся создавать два типа класса CarA для разных двигателей, например электрического Electric и например Gas вместо того, чтобы переиспользовать тот же тип но у которого можно заменить двигатели.
  • Такая жёсткая зависимость от класса Engine затрудняет тестирование. CarA самостоятельно создаёт экземпляр Engine , и это не даёт модифицировать Engine для разных тест-кейсов.

А теперь взгляните на код класcа CarB

В данном примере функция main использует Car . Так как CarB зависит от Engine , приложение создаёт экземпляр Engine и использует его для создания экземпляра класса Car . Использование DI-подхода в данном примере даёт следующие плюсы:

  • Переиспользование CarB . Можно передать любые имплементации Engine в класс Car .
  • Возможность тестирования класса CarB . Можно создать различные экземпляры Engine для разных тест-кейсов.

Класс CarA инициализирует поле Engine самостоятельно, в то время, как класс CarB просто использует переданный извне объект класса Engine . То есть, получается что внедрение зависимости — это подход, при котором класс или какая-то другая сущность может использовать любой экземпляр нужного объекта извне, не создавая самостоятельно. Короче говоря, класс не должен знать как создать необходимый ему экземпляр (то есть объект от которого он зависит) — а должен просто использовать переданную извне зависимость.

В полном соответствии с принципом единственной обязанности объект отдаёт заботу о построении требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму.

Следуя принципу DI, вы строите фундамент хорошей архитектуры вашего приложения. Используя DI вы автоматически получаете следующие преимущества:

  • Возможность переиспользования кода
  • Простота при рефакторинге
  • Лёгкость написания тестов

Надеюсь, теперь стало понятно что такое DI и почему в каждой вакансии все требуют знание какого-либо DI-фреймворка. Давайте теперь рассмотрим какие есть способы внедрения зависимостей (DI) в Android:

  • Внедрение зависимости через конструктор (Constructor Injection). Этот способ мы рассмотрели выше. В этом способе нужно передать необходимую зависимость в конструктор.
  • Внедрение зависимости через поле. (Field Injection (or Setter Injection)). Некоторые класс из Android framework такие как Activity или Fragments создаются системой, так что первый способ использовать нельзя. Внедрение зависимости через поле позволяет передать зависимость уже после того как класс будет создан. Примерно это будет выглядеть вот так:

А теперь расскажите что такое ServiceLocator

Альтернативой DI является паттерн, или как многие его называют антипаттерн ServiceLocator. Этот паттерн также как и DI уменьшает связанность кода. Однако, на мой взгляд имеет существенные недостатки. Суть паттерна в том, что вместо того, чтобы application или некий control flow передавал зависимости классу, сам класс вызывает некий метод для инициализации нужной ему зависимости. Это выглядит так:

То есть мы создали некую фабрику, которую класс Car использует для получения нужной ему зависимости.

Недостатки ServiceLocator

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

Или же ServiceLocator может передаваться требуемым классам через конструктор или свойство в виде объекта класса или интерфейса.

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

Недостатками паттерна ServiceLocator являются:

  • Неясный контракт класса
  • Неопределенная сложность класса

Если класс принимает экземпляр сервис локатора, или, хуже того, использует глобальный локатор, то этот контракт, а точнее требования, которые нужно выполнить клиенту класса, становятся неясными. То есть если у вас миллион методов получения того или иного экземпляра, то какой именно экземпляр нужен вашему классу можно понять, лишь прочитав исходный код класса клиента (в данном примере нужно посмотреть исходный код класса Car )

И второй недостаток, это то, что когда наш класс использует сервис локатор, то стабильность класса становится неопределенной. Наш класс, теоретически, может использовать что угодно, поэтому изменение любого класса (или интерфейса) в нашем проекте может затронуть произвольное количество классов и модулей.

Ок. Ясно. Так что в итоге использовать?

Google рекомендует делать так, как в табличке ниже. Но, правда они не говорят, что есть еще Koin (кстати на AndroidSchool есть бесплатный туториал по работе c Koin), Toothpick, Kodein и другие инструменты для программной реализации DI.

Источник

Dependency injection

От переводчика

Представляемый вашему вниманию перевод открывает серию статей от Jakob Jenkov, посвященных внедрению зависимостей, или DI. Примечательна серия тем, что в ней автор, анализируя понятия и практическое применение таких понятий как «зависимость», «внедрение зависимостей», «контейнер для внедрения зависимостей», сравнивая паттерны создания объектов, анализируя недостатки конкретных реализаций DI-контейнеров (например, Spring), рассказывает, как пришел к написанию собственного DI-контейнера. Таким образом, читателю предлагается познакомиться с довольно цельным взглядом на вопрос управления зависимостями в приложениях.

Читайте также:  Камера с ручными настройками для андроида

В данной статье сравнивается подход к настройке объектов изнутри и извне (DI). По смыслу настоящая статья продолжает статью Jakob Jenkov Understanding Dependencies, в которой дается определение самому понятию «зависимости» и их типам.

Серия включает в себя следующие статьи

Внедрение зависимостей

«Внедрение зависимостей» — это выражение, впервые использованное в статье Мартина Фаулера Inversion of Control Containers and the Dependency Injection Pattern. Это хорошая статья, но она упускает из виду некоторые преимущества контейнеров внедрения зависимостей. Также я не согласен с выводами статьи, но об этом — в следующих текстах.

Объяснение внедрения зависимостей

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

UPD: после обсуждения представленных автором фрагментов кода с flatscode и fogone, я принял решение скорректировать спорные моменты в коде. Изначальный замысел был в том, чтобы не трогать код и давать его таким, каков он написан автором. Оригинальный авторский код в спорных местах закомментирован с указанием «в оригинале», ниже дается его исправленная версия. Также оригинальный код можно найти по ссылке в начале статьи.

Этот DAO (Data Access Object), MyDao нуждается в экземпляре javax.sql.DataSource для того, чтобы получить подключения к базе данных. Подключения к БД используются для чтения и записи в БД, например, объектов Person.

Заметьте, что класс MyDao создает экземпляр DataSourceImpl, так как нуждается в источнике данных. Тот факт, что MyDao нуждается в реализации DataSource, означает, что он зависит от него. Он не может выполнить свою работу без реализации DataSource. Следовательно, MyDao имеет «зависимость» от интерфейса DataSource и от какой-то его реализации.

Класс MyDao создает экземпляр DataSourceImpl как реализацию DataSource. Следовательно, класс MyDao сам «разрешает свои зависимости». Когда класс разрешает собственные зависимости, он автоматически также зависит от классов, для которых он разрешает зависимости. В данном случае MyDao завсист также от DataSourceImpl и от четырех жестко заданных строковых значений, передаваемых в конструктор DataSourceImpl. Вы не можете ни использовать другие значения для этих четырех строк, ни использовать другую реализацию интерфейса DataSource без изменения кода.

Как вы можете видеть, в том случае, когда класс разрешает собственные зависимости, он становится негибким в отношении к этим зависимостям. Это плохо. Это значит, что если вам нужно поменять зависимости, вам нужно поменять код. В данном примере это означает, что если вам нужно использовать другую базу данных, вам потребуется поменять класс MyDao. Если у вас много DAO-классов, реализованных таким образом, вам придется изменять их все. В добавок, вы не можете провести юнит-тестирование MyDao, замокав реализацию DataSource. Вы можете использовать только DataSourceImpl. Не требуется много ума, чтобы понять, что это плохая идея.

Давайте немного поменяем дизайн:

Заметьте, что создание экземпляра DataSourceImpl перемещено в конструктор. Конструктор принимает четыре параметра, это — четыре значения, необходимые для DataSourceImpl. Хотя класс MyDao все еще зависит от этих четырех значений, он больше не разрешает зависимости сам. Они предоставляются классом, создающим экземпляр MyDao. Зависимости «внедряются» в конструктор MyDao. Отсюда и термин «внедрение (прим. перев.: или иначе — инъекция) зависимостей». Теперь возможно сменить драйвер БД, URL, имя пользователя или пароль, используемый классом MyDao без его изменения.

Внедрение зависимостей не ограничено конструкторами. Можно внедрять зависимости также используя методы-сеттеры, либо прямо через публичные поля (прим. перев.: по поводу полей переводчик не согласен, это нарушает защиту данных класса).

Класс MyDao может быть более независимым. Сейчас он все еще зависит и от интерфейса DataSource, и от класса DataSourceImpl. Нет необходимости зависеть от чего-то, кроме интерфейса DataSource. Это может быть достигнуто инъекцией DataSource в конструктор вместо четырех параметров строкового типа. Вот как это выглядит:

Теперь класс MyDao больше не зависит от класса DataSourceImpl или от четырех строк, необходимых конструктору DataSourceImpl. Теперь можно использовать любую реализацию DataSource в конструкторе MyDao.

Цепное внедрение зависимостей

Пример из предыдущего раздела немного упрощен. Вы можете возразить, что зависимость теперь перемещена из класса MyDao к каждому клиенту, который использует класс MyDao. Клиентам теперь приходится знать о реализации DataSource, чтобы быть в состоянии поместить его в конструктор MyDao. Вот пример:

Как вы можете видеть, теперь MyBizComponent зависит от класса DataSourceImpl и четырех строк, необходимых его конструктору. Это еще хуже, чем зависимость MyDao от них, потому что MyBizComponent теперь зависит от классов и от информации, которую он сам даже не использует. Более того, реализация DataSourceImpl и параметры конструктора принадлежат к разным слоям абстракции. Слой ниже MyBizComponent — это слой DAO.

Решение — продолжить внедрение зависимости по всем слоям. MyBizComponent должен зависеть только от экземпляра MyDao. Вот как это выглядит:

Снова зависимость, MyDao, предоставляется через конструктор. Теперь MyBizComponent зависит только от класса MyDao. Если бы MyDao был интерфейсом, можно было бы менять реализацию без ведома MyBizComponent.

Такой паттерн внедрения зависимости должен продолжается через все слои приложения, с самого нижнего слоя (слоя доступа к данным) до пользовательского интерфейса (если он есть).

Источник

Dagger 2. Лечим зависимости по методике Google


Автор: Константин Марс
Senior Developer @ DataArt,
Co-Organizer @ GDG Dnipro

Dependency Injection

Что, зачем и когда это нужно

Создание классов порождает…

  • Композиция — не наследование.
  • Ссылки не будут пустовать.

Возможность создавать объекты…

Вы можете создать объект, задать имя машины и создать какой-нибудь новый двигатель.

Доступно создание разных объектов, например, создание двигателя другого типа или просто другого двигателя.

Предположим, вы можете создать два разных объекта, которые будете использовать. В данном случае, тот самый двигатель от «Патриота». Соответственно, если вы поставите этот двигатель в Jeep Grand Cheerokee — это будет немного странно. Но, тем не менее, вы можете это сделать. При этом используется так называемый паттерн «композиция », когда сущности, которые вы создаете, будут включаться в другую сущность, и это будет, как вы видите, не наследование, а именно композиция.

Здесь все очень просто: если вы посмотрите на SuperTunedEngine, поймете, что на самом деле он является наследником определенного типа, который уже объявлен заранее, а также, возможно, является реализацией интерфейса — для нас это непринципиально. В данном случае Engine может быть интерфейсом.

Читайте также:  Мобильный opera для андроид

Глядя на два объявления, вы видите: мы можем сделать так, что два объекта будут зависеть от какого-либо другого объекта. Собственно, это и есть зависимость. Таким образом, возможность создавать объекты порождает зависимости — довольно банальная вещь.

И… зависимости

Car depends on Engine. Engines may vary. We’ll probably need different engines for testing and production.

Как вы видите на схеме (изображение не из нашего примера), зависимости бывают очень разные. У вас будут зависимые сервисы, зависимые activity, презентеры, вью, контроллеры. Все эти сущности переплетены между собой зависимостями. Если попытаться выразить это графически, получится примерно то, что вы видите сейчас на картинке.
В реальной рабочей системе зависимостей будет гораздо больше. Тесты, которые проводят известные компании, предоставляющие инструментарий для тестирования Android-приложений, показывают, что зависимостей, даже в простых, на первый взгляд, приложениях, бывают тысячи. В среднем тысячи и десятки тысяч зависимостей встречаются даже в самых простых приложениях. Чтобы реализовывать эти зависимости как можно более эффективно, не инстанцируя каждый раз внутри своего класса какие-то другие классы и не дописывая кучу кода, который будет повторяться и добавлять вам лишнюю работу, существует инструмент Dagger.

Dagger and JSR-330 Standart

Аннотация Inject

Dagger основан на стандарте JSR-330. Этот стандарт Google использует очень давно, и это — стандарт для Java Injection.

Немного еще НЕ истории

  • Dagger 2 — Google, Greg Kick
  • Dagger — Square, Jake Wharthon
  • Guice — Google, Jesse Wilson

Заглянем немного в историю: Google когда-то создал такой продукт как Guice (в народе его называют «Джус», а в наших широтах — «Гусь»). Guice работал с рефлексией, он следовал аннотациям, но впоследствии разработчики из Square усовершенствовали систему, которая была в Guice и создали Dagger1.
Dagger1 был крутым инструментом, но, как показывает практика, и тут можно что-то улучшить. Кстати, Dagger1 тоже использовал рефлексию. И в 2015 г. разработчики из Google выпустили Dagger2. Кажется, что еще совсем недавно Jake Wharton (известный разработчик из компании Square) анонсировал его выпуск с прицелом на осень — обещание выполнено, у нас есть качественный и опережающий конкурентов по результатам тестов продукт.

Инверсия управления (англ. Inversion of Control, IoC)

Вернемся к стандартам и терминологии. Итак, у нас есть продукт, который появился в ходе эволюции. Он использует JSR-330, который, предоставляет целый ряд аннотаций. Кроме того, он следует определенным принципам, своего рода паттернам разработки, один из которых — Inversion of control (IoC).
Процесс предоставления внешней зависимости программному компоненту является специфичной формой «инверсии контроля» (англ. Inversion of control, IoC), когда она применяется к управлению зависимостями. В соответствии с принципом single responsibility объект отдаёт заботу о построении требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму.
Эта вещь связана с архитектурными паттернами. Мы должны писать приложение таким образом, чтобы внутренние классы, связанные с доменной логикой, не зависели от внешних классов, чтобы приложение было написано основываясь на интерфейсах. Таким образом реализуется разграничение зоны ответственности. Обращаясь к какой-то реализации, мы обращаемся, в первую очередь, к интерфейсу. Inversion of Control реализуется через Dependency Injection собственно сам инструментарий называется Dependency Injection (DI).

Reflection vs Compile time

Dagger2 использует кодогенерацию, в отличие от Dagger1, который использовал рефлексию.

JSR-330

JSR-330 a.k.a. javax.inject

  • Inject, Qualifier, Scope. etc.
  • Standardized Dependency Injection API
  • Reference Implementation: Google Guice 2.0
  • Also supported by Spring since 3.0
  • Defines API, not injector implementation or configuration

JSR описывает не только аннотацию Inject, но и предоставляет целый пакет аннотаций, которые позволят вам декларировать каким образом будут взаимодействовать сущности для обеспечения Dependency Injection.
Например, я рассказываю об определенном семействе Dependency Injection-продуктов, которые следуют этому стандарту. Есть другие продукты, которые этому стандарту не следуют, о них мы сегодня говорить не будем, но они существуют. Есть Inject, Qualifier, Scope — о них мы поговорим позже. Эти аннотации не были созданы только для Dagger2, они существуют и для других инжекторов, например, Guice.

Итак, пришло время добавить в наш код немного магии…

Мы начнем с того, что аннотируем члены класса аннотацией inject. Все достаточно просто. Чтобы инстанцировать в дальнейшем эти зависимости и наш инструментарий Dependency Injection смог правильно подобрать куда именно инстанцировать и что, мы должны также аннотировать конструктор. Здесь ситуация становится немного интереснее.

Обратите внимание на конструктор по умолчанию

Самый простой способ обеспечить инжекцию — создать конструктор по умолчанию. Затем аннотировать инжектом сам конструктор по умолчанию и те члены, которые требуют инстанцирования этого класса. Это делается очень просто.

Конструктор с параметрами — хорошее место для модификаций

В реальной жизни нам понадобятся конструкторы с параметрами. Некоторые из них система сможет подобрать автоматически, если у них есть конструкторы по умолчанию. А некоторые, например, тот же Engine, возможно придется конструировать вручную.
Также вы будете инжектировать презентеры с помощью таких конструкторов, это очень часто используется в MVP (Model-View-Presenter).

И все же — как это заставить работать?

Структура инжекции Dagger2.0

Структура инжекции — взаимосвязь компонентов Dagger, которые позволяют нам объединить аннотации inject и объединить объявления классов.

Компоненты и Модули


Pic. author — Miroslaw Stanek from Azimo
http://frogermcs.github.io/dagger-graph-creation-performance/
Структура инжекции Dagger включает в себя компоненты и модули. Если вы посмотрите на картинку (из статьи Мирослава Станек из компании Azima), увидите, что компоненты — контейнеры для модулей, причем внутри одного компонента могут быть другие. Позже мы увидим, что компоненты, которые вложены, называются субкомпоненты (@SubСomponent). И называть их просто «компоненты» мы не можем по правилам инжекции.

Модуль — коллекция генераторов

Аннотация Module — аннотация, которая говорит, что вот эта сущность — вот этот класс — является модулем, который будет генерировать инстансы объектов.
Здесь тоже все достаточно просто. Аннотации, которые генерируют, — это аннотация provides. Аннотация provides просто указывает, что данный метод модуля будет поставлять вам сущность. Самое интересное будет происходить внутри этого метода.
Вам необходимо будет, следуя этим правилам, каким-то образом инстанцировать объект. Некоторые объекты будут зависеть друг от друга. Некоторые объекты будут зависеть от членов класса модуль, которые вы можете хранить в модуле. Например, тот же контекст, вы можете положить в модуль. Модуль о нем помнит и потом, при инстанцировании тех же самых прензентеров, вы будете генерировать новые сущности презентеров на основе контекста, который модуль запомнил один раз при его создании.
Как вы видите, у модуля есть конструктор. В данном случае вместо контекста мы передаем Application. При создании чего-то нового можем возвращать то, что хранится в самом модуле.

Читайте также:  Андроид для настольных компьютеров

Что такое синглетон (англ. Singleton)?

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

@Component

Компонент — хост для модулей, инжектор для классов, корень дерева зависимостей.

С компонентом все немного интереснее. Компонент должен учитывать время жизни модулей, которые он включает. Если мы попробуем использовать синглтон для компонента, который использует время жизни инстанцирования, возникнут конфликты. Поэтому нужно четко понимать, что, например, компонент для Application, будет синглетоном, потому что объект класса Application существует в единственном экземпляре, и существуют все время жизни приложения. Для activity, например, это тоже может быть синглетон, и его время жизни будет привязано к времени жизни activity. При необходимости существует возможность аннотировать компонент дополнительной аннотацией Singleton. Есть список модулей, который включает в себя это компонент.
Например, в Activity будет аннотация inject. В компоненте должны быть указаны модули, которые делают provides этой activity. Вы должны обязательно указать в компоненте, куда мы инжектируем. Т. е. мы должны указать конкретный класс, причем, обратите внимание, что здесь нельзя, например, написать BaseActivity как базовый класс, потому что тогда инжекция произойдет только в Base aActivity, а в MainActivity, куда нужно, например, проинжектить какой-то презентер, правила будут чуть-чуть другими.

Метод inject — описание того, кто зависит. Модули — описание тех, кто предоставляет зависимости.
Давайте вернемся к модулю. Модуль объявляется как класс. Важно заметить, что модуль — реальный класс, который имеет настоящие ссылки на реальные объекты. И он создается вами вручную при объявлении компонента, при связке. Компонент, в свою очередь, — объект, который генерирует Dagger. Как раз в этот момент происходит магия кодогенерации. Поэтому компонент объявляется как интерфейс.

Инициализация компонента generated code used

Вот, например, DaggerAppComponent инициализируется внутри нашего приложения. Обратите внимание, generated code used (инициализация компонента) значит, что мы используем генерированный код. Например, можно обнаружить DaggerAppComponent. Как вы видели ранее, никакого префикса Dagger не было. Откуда же он появился? Да, Dagger сгенерировал код. Он его генерирует достаточно быстро. Если вы случайно поломаете инжекцию, о которой мы сейчас говорим (о ее структуре), в итоге у вас DaggerAppComponent не появится. Если вы допустили небольшую ошибку и неправильно указали класс, генерация не сработает — DaggerAppComponent не появится, и вся магия, которая обеспечивает нам привязку наших activity и других классов, не заработает без сгенерированного класса. Потому что компонент является корнем всего дерева — это основа. И без него все остальное не работает. Следует внимательно относиться к тому, как мы строим инжекцию до этого, и правильно использовать компонент.
Также следует отметить, что у компонента есть builder. Builder — паттерн проектирования. Мы понимаем, что у билдера есть какие-то аргументы, которые определяют, как будет строиться дальше наш компонент, например, метод AppModule — автоматически сгенерированный метод, который принимает в качестве аргумента инстанс-классом AppModule. Модуль мы создаем руками и задаем для него параметры. И вызываем метод build для получения AppComponent. В этой ссылке есть пример из реального кода: http://github.com/c-mars/Dagger2Scopes.git.

Inject This! 🙂

Puttin’ magic will work only after injection… 🙂

У класса Application есть методы, которые предоставляют доступ к нему. Тем более, это не статический метод, вы можете просто получить из get application контекст. Можете его прикастить к своему классу — получится то же самое и никакой магии тут не будет. Но, что для действительно важно, у нас будет этот getAppComponent.
Идея в том, что Application хранит AppComponent. Мы вызываем какие-то дополнительные методы на этом компоненте, и затем применяем метод inject. Как вы заметили, этот тот инжект с указанием конкретного класса, который мы объявили в компоненте. В данном случае это — класс LoginActivity. Вы видите в аннотации инжект, видите, как мы заинжектили зависимости. Магия заработает только после инжекшена.

Custom Scopes и эффективное управление памятью

Custom Scopes, как таковые, служат для того, чтобы обеспечить вашим истанцированным классам определенное время жизни.

Жизненный цикл объектов


Pic. author — Miroslaw Stanek from Azimo
http://frogermcs.github.io/dagger-graph-creation-performance
Например, у вас есть activity, и они живут довольно недолго, с одного на экрана на другой переходите и все убиваете. То есть все, что заинжекшено в activity, вполне можно после этого почистить, и приложение будет потреблять меньше памяти. Какой-то класс пользовательских данных, например, User, будет жить между логинами. Application Scope — самый главный, корневой scope, живущий дольше всех.

И еще раз та же матрешка
Компонент имеет область жизни (scope)


Pic. author — Miroslaw Stanek from Azimo
http://frogermcs.github.io/dagger-graph-creation-performance/

This mysterious ‘plus’.

Теперь обратим внимание на плюс.

Объявление субкомпонента

Аннотация Scope позволяет вам генерировать скоупы определенного времени жизни. Например, ActivityScope будет жить столько, сколько живет activity. Им аннотируются компоненты как субкомпоненты.

Но ведь там был модуль!

Данные внутри описываются также, как в корневом компоненте. За исключением одной вещи: когда вы вызовете плюс, передадите туда модуль и инстанцируете его, вы получите подкомпонент, который вам нужен.

Добавление субкомпонента к корню дерева зависимостей

Для чего это используется? Чтобы скоупы, которые можно объявлять как интерфейсы, ограничивали время жизни наших объектов.

Аннотация Scope

Этот вид Scope будет ограничивать время жизни статически, в зависимости от того, куда вы заинжектились. А другой будет ограничивать динамически.
Динамический означает, что вы будете управлять им вручную, и все будет удаляться с помощью garbage collector («сборщик мусора»).
Мы аннотируем компонент, необходимый скоупам.

@ActivityScope

@UserScope, например, как он будет работать этот тот самый скоуп, который имеет @Retention(RUNTIME). Им можно будет управлять вручную.

@UserScope

Чтобы им управлять вручную, вы сохраняете ссылку на компонент внутри приложения рядом с AppComponent.

Он создается с помощью особого метода, пример кода, который вы можете увидеть. Затем код почистили, отправили его в релиз, и garbage collector его удалит. Когда это происходит? Когда пользователь вышел из системы («вылогинился»). В следующий раз, когда вы вызовете еще один createUserComponent, этот компонент создастся еще раз с другими данными юзера.

Источник

Оцените статью