Серия приложений Kriptofolio - Часть 4

Внедрение зависимостей значительно улучшит ваш код. Это делает ваш код более модульным, гибким и тестируемым. На самом деле его название звучит сложнее, чем стоящая за ним идея.

В этой части серии мы узнаем о внедрении зависимостей. Затем мы реализуем его в приложении «Криптофолио» (ранее «Мои крипто-монеты»). Мы собираемся использовать Dagger 2. Dagger 2 - самый популярный фреймворк для внедрения зависимостей с открытым исходным кодом для Android. Это ценный навык для создания современных приложений, даже если кривая обучения достаточно сложна.

Содержание серии

Что такое внедрение зависимостей?

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

Как вы видите из этого примера, ваш класс MyAppClass будет напрямую зависеть от конкретной конфигурации и реализации вашего библиотечного класса MyLibrary. Что, если вы когда-нибудь захотите использовать вместо этого стороннюю библиотеку? Что, если вы хотите иметь другой класс, в котором вы хотели бы использовать точно такую ​​же конфигурацию библиотеки? Каждый раз вам придется искать свой код, находить точное место и менять его. Это всего лишь несколько примеров.

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

Вот и все, это очень примитивный пример внедрения зависимостей. Вместо того, чтобы создавать и настраивать новый объект класса MyLibrary внутри вашего класса MyAppClass, вы просто передаете или вставляете его в конструктор. Так что MyAppClass может быть совершенно безответственным за MyLibrary.

Что такое Dagger 2?

Dagger - это полностью статическая среда внедрения зависимостей с открытым исходным кодом во время компиляции как для Java, так и для Android. В этой статье я расскажу о его второй версии, которую поддерживает Google. Square создала свою более раннюю версию.

Dagger 2 считается одним из самых эффективных фреймворков для внедрения зависимостей, созданных на сегодняшний день. На самом деле, если вы сравните Dagger 1, Dagger 2 и Dagger 2.10, вы обнаружите, что каждая реализация отличается. Вам нужно переучивать его каждый раз, так как авторы внесли существенные изменения. При написании этой статьи я использую версию Dagger 2.16, и мы собираемся сосредоточиться только на ней.

Как вы теперь понимаете о внедрении зависимостей, наши классы не должны создавать или иметь зависимости. Вместо этого им нужно получать все извне. Таким образом, при использовании Dagger 2 этот фреймворк предоставит все необходимые зависимости.

Он делает это, генерируя для нас много шаблонного кода. Этот сгенерированный код будет полностью отслеживаемым и имитирует код, который пользователь может написать вручную. Dagger 2 написан на Java, и код, сгенерированный его процессором аннотаций, также будет кодом Java.

Однако он работает с Kotlin без каких-либо проблем и модификаций. Помните, что Kotlin полностью совместим с Java. По сравнению с аналогичными фреймворками Dagger 2 менее динамичен. Он работает во время компиляции, а не во время выполнения с отражением. Отражения вообще нет. Все это означает, что эту структуру будет сложнее настроить и изучить. Это обеспечит повышение производительности с безопасностью во время компиляции.

Внедрение зависимостей вручную без инструментов

Возможно, вы заметили в приложении My Crypto Coins исходный код из предыдущей части, что есть фрагмент кода для внедрения объектов без использования каких-либо инструментов внедрения зависимостей. Он отлично работает, и этого решения было бы достаточно для такого небольшого приложения, как это. Взгляните на пакет утилит:

Как видите, всю работу выполняет этот класс. Он создаст фабрики ViewModel для действий или фрагментов, которые в них нуждаются.

Затем вы используете такой класс InjectorUtils, где вам нужно получить конкретную фабрику ViewModel:

Как видите, наш MainListFragment класс даже не знает о CryptocurrencyRepository или AppDatabase. Он получает успешно построенную фабрику из класса InjectorUtils. На самом деле это один простой способ сделать это. Мы собираемся избавиться от него и узнать, как настроить инструмент Dagger 2 для расширенного внедрения зависимостей. Если это приложение расширится функциональностью и кодом, я не сомневаюсь, что мы начнем очень быстро замечать преимущества использования профессионального фреймворка для внедрения зависимостей по сравнению с ручным решением.

Итак, давайте удалим InjectorUtils класс прямо сейчас и узнаем, как настроить Dagger 2 в исходном коде приложения My Crypto Coins.

Внедрение зависимостей для MVVM с помощью Kotlin

Как настроить Dagger 2 с моделями просмотра, действиями и фрагментами

Теперь мы рассмотрим пошаговую настройку Dagger 2 в проекте приложения My Crypto Coins.

Для начала вы должны включить собственный Инструмент обработки аннотаций Kotlin (kapt). Затем добавьте специальные зависимости Dagger 2.

Вы можете сделать это, добавив эти строки в свой файл gradle:

Плагин Kapt позволит компилятору генерировать классы-заглушки, необходимые для взаимодействия между Java и Kotlin. Для удобства мы определим конкретную версию Dagger 2 в отдельном файле gradle, как мы делаем это со всеми нашими зависимостями.

Чтобы найти последнюю доступную версию, проверьте выпуски в официальном репозитории Dagger 2 на Github.

Теперь создайте свое приложение App класс.

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

Для приложения My Crypto Coins мы уже создали класс приложения ранее.

Затем обновите файл манифеста, чтобы включить ваш App класс.

Пропустите это, если вы уже делали это раньше.

Для приложения My Crypto Coins мы также уже установили класс App в манифесте ранее.

Теперь давайте создадим новый пакет под названием dependencyinjection.

Здесь мы сохраним все файлы, относящиеся к реализации Dagger.

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

Как видите, для создания модуля Dagger нам нужно аннотировать его специальной аннотацией @Module. В проектах обычно есть несколько модулей Dagger. Для одного из них характерно предоставление зависимостей для всего приложения. Этот AppModule будет использоваться для инициализации объектов, используемых в нашем приложении, таких как база данных комнат, модернизация, общие настройки и т. Д.

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

Нам нужно использовать специальную аннотацию Dagger @Provides. Он сообщает Dagger, что метод предоставляет определенный тип зависимости, в нашем случае объект Context. Поэтому, когда где-то в приложении мы запрашиваем внедрение контекста, AppModule - это то место, где его находит Dagger. И имена наших методов не имеют значения, поскольку Dagger заботится только о типе возвращаемого значения. Это общепринятая практика называть метод с префиксом provide, но это может быть что угодно.

Аннотации @Singleton, которые вы видите применительно к тому же методу, не являются частью аннотаций Dagger. Он содержится внутри пакета javax. Эта аннотация сообщает Dagger, что должен быть только один экземпляр этой зависимости.

Вам не нужно писать шаблонный код, чтобы проверить, доступен ли уже другой экземпляр объекта. При создании кода Dagger будет обрабатывать всю эту логику за вас из-за этой аннотации. Обратите внимание, что наш AppModule включает еще один модуль ViewModelsModule. Давай создадим это сейчас.

Создайте модуль класса ViewModelsModule. Этот модуль будет отвечать за предоставление ViewModels для всего вашего приложения.

Этот модуль использует множественные привязки карты функций Dagger 2. Используя его, мы вносим объекты по нашему выбору в карту, которая становится доступной в любом месте нашего приложения. Используя комбинацию аннотаций Dagger @Binds, @IntoMap и нашей пользовательской аннотации @ViewModelKey (которую мы собираемся создать), мы создаем запись внутри нашей карты с ключом MainViewModel::class и значением MainViewModel instance. Мы связываем конкретную фабрику с помощью некоторого общего ViewModelFactory класса. Нам нужно создать этот класс.

Создайте собственный класс аннотаций ViewModelKey.

Этот класс используется для привязки ViewModels в ViewModelsModule. Специальная аннотация @ViewModelKey представляет ключ нашей карты. Нашим ключом может быть только класс, унаследованный от ViewModel.

Создайте класс ViewModelFactory.

Этот ViewModelFactory служебный класс, который помогает вам динамически создавать модели просмотра. Здесь вы предоставляете сгенерированную карту в качестве аргумента. Метод create() сможет выбрать нужный экземпляр на карте.

Создайте модуль класса ActivityBuildersModule.

Этот модуль отвечает за построение всех ваших действий. Он будет генерировать AndroidInjector для всех действий, определенных в этом классе. Затем объекты можно вводить в действия с помощью AndroidInjection.inject(this) в функции onCreate из жизненного цикла действия. Обратите внимание, что этот модуль также использует другой отдельный модуль, отвечающий за фрагменты. Далее мы создадим этот модуль.

Создайте модуль класса MainListFragmetBuildersModule.

Этот модуль построит все ваши фрагменты, относящиеся к MainActivity. Он сгенерирует AndroidInjector для всех фрагментов, определенных в этом классе. Объекты можно вводить во фрагменты с помощью AndroidSupportInjection.inject(this) в функции onAttach из жизненного цикла фрагмента.

Создайте AppComponent компонент интерфейса.

Компонент - очень важный интерфейс. Это позволит всему вышеперечисленному начать работать вместе. Он делает это путем подключения объектов к их зависимостям. Dagger будет использовать этот интерфейс для генерации кода, необходимого для внедрения зависимости.

Чтобы создать класс компонента, вам нужно будет использовать аннотацию Dagger @Component. На входе он принимает список модулей. Другая аннотация @Component.Builder позволяет нам привязать некоторый экземпляр к компоненту.

Затем создайте объект графика.

На данный момент у вас есть все ваши модули и настройки компонентов. Вы можете сгенерировать свой объект графика, выбрав Build - ›Make Module внутри вашей Android Studio IDE. Это поколение понадобится нам для будущих шагов.

Теперь создайте Injectable интерфейс.

Это также понадобится для будущих шагов. Injectable интерфейс должен быть реализован действиями или фрагментами, которые мы хотим внедрять автоматически.

Создайте новый вспомогательный класс с именем AppInjector.

Это простой вспомогательный класс, позволяющий избежать вызова метода inject для каждого действия или фрагмента.

Затем настройте класс App, который мы уже создали ранее.

Поскольку в приложении есть действия, нам необходимо реализовать интерфейс HasActivityInjector. Если вы видите ошибку, вызванную Android Studio на DaggerAppComponent, это потому, что вы не создали новый файл, как было указано на предыдущем шаге.

Итак, настройте MainActivity, чтобы внедрить основную фабрику ViewModel и добавить поддержку инъекции фрагментов.

Поскольку у наших действий есть дочерние фрагменты, нам необходимо реализовать HasSupportFragmentInjector интерфейс. Это нам тоже нужно, потому что мы планируем делать инъекции в наши фрагменты. Наша деятельность не должна знать о том, как она вводится. Мы используем строку кода AndroidInjection.inject(this) внутри метода переопределения onCreate().

Вызов метода inject() заставит Dagger 2 найти синглтоны в графе зависимостей, чтобы попытаться найти соответствующий возвращаемый тип. Однако нам не нужно писать здесь какой-либо код, потому что это делается за нас с помощью ранее созданного вспомогательного класса AppInjector, который мы инициализировали внутри нашего класса приложения.

Затем настройте MainListFragment, чтобы внедрить основную фабрику ViewModel.

Подобно действиям, если мы хотим, чтобы наш фрагмент был инъекционным, тогда в его onAttach метод мы должны написать код AndroidSupportInjection.inject(this). Но опять же, это работа помощника AppInjector, поэтому мы можем пропустить это. Просто обратите внимание, что нам нужно добавить интерфейс Injectable, который мы создали ранее, чтобы помощник работал.

Поздравляем, мы добавили Dagger 2 в проект приложения My Crypto Coins. Конечно, эта статья представляет собой краткое руководство по немедленному развертыванию Dagger 2 в вашем приложении, но не является ее подробным описанием. Я рекомендую вам продолжить изучение этой темы, если вы не понимаете основ.

Репозиторий

Ознакомьтесь с исходным кодом обновленного приложения «Kriptofolio» (ранее «My Crypto Coins») на GitHub.

Посмотреть исходный код на GitHub

Ачу! Спасибо за прочтение! Изначально я опубликовал этот пост в своем личном блоге www.baruckis.com 7 октября 2018 г.