- Android hilt release notes
- Hilt Design Overview
- Hilt is stable! Easier dependency injection on Android
- Simpler than Dagger
- Less boilerplate
- Designed for Android
- Standardization of components and bindings
- Integration with other Jetpack libraries
- Resources to learn Hilt
- Hilt еще один DI?
- Зачем?
- Особенности
- Application обязателен
- Иерархическая зависимость
- Объявление модулей
- Кастомные Component и Subcomponent
- Поддержка многомодульности
- Ограничения для @AndroidEntryPoint
- Что внутри
- Как устроено хранение ActivityRetainedComponent
- Android hilt release notes
- Hilt+Retrofit+Coroutine
- Подключение зависимостей
- Теперь расставим аннотации
- Retrofit
- Передаём данные во ViewModel
Android hilt release notes
Hilt provides a standard way to incorporate Dagger dependency injection into an Android application.
The goals of Hilt are:
- To simplify Dagger-related infrastructure for Android apps.
- To create a standard set of components and scopes to ease setup, readability/understanding, and code sharing between apps.
- To provide an easy way to provision different bindings to various build types (e.g. testing, debug, or release).
Hilt Design Overview
Hilt works by code generating your Dagger setup code for you. This takes away most of the boilerplate of using Dagger and really just leaves the aspects of defining how to create objects and where to inject them. Hilt will generate the Dagger components and the code to automatically inject your Android classes (like activities and fragments) for you.
Hilt generates a set of standard Android Dagger components based off of your transitive classpath. This requires marking your Dagger modules with Hilt annotations to tell Hilt which component they should go into. Getting objects in your Android framework classes is done by using another Hilt annotation which will generate the Dagger injection code into a base class that you will extend. For Gradle users, extending this class is done with a bytecode transformation under the hood.
In your tests, Hilt generates Dagger components for you as well just like in production. Tests have other special utilities to help with adding or replacing test bindings.
Источник
Hilt is stable! Easier dependency injection on Android
Hilt, Jetpack’s recommended dependency injection (DI) solution for Android apps, is already stable! That means Hilt is fully ready to be used in production. Hilt is simpler than Dagger, enables you to write less boilerplate code, it’s designed for Android and has integration with multiple Jetpack libraries. Several companies have already started taking advantage of Hilt in their apps.
Hilt was first released as alpha in June 2020 with the mission of defining a standard way to do DI in your Android apps and since then, we’ve received tons of feedback from developers. That not only improved the library, but also, it let us know that we’re working on the right problems.
Instead of creating dependency graphs by hand, and manually injecting and passing around types where needed, Hilt automatically generates all that code for you at compile time by means of annotations. Hilt can help you get the most out of DI best practices in your app by doing the hard work and generating all that boilerplate you would’ve needed to write otherwise. Also, as it’s fully integrated with Android, Hilt manages the lifecycle of the dependency graphs associated with the Android framework classes automatically for you.
Let’s see Hilt in action with a quick example! After setting Hilt up, using it in your project from scratch to inject a ViewModel in an Activity is as easy as adding few annotations to your code as follows:
Apart from what’s mentioned above, why should you use Hilt in your Android app?
Simpler than Dagger
Hilt is built on top of the popular DI library Dagger so benefits from the compile time correctness, runtime performance, scalability, and Android Studio support that Dagger provides. Some Dagger annotations, such as @Inject to tell Dagger and Hilt how to provide an instance of a type, are often used in Hilt. But Hilt is simpler than Dagger!
“I highly recommend leveraging Dagger for dependency injection in Android applications. However, pure vanilla Dagger can lead to too much room for creativity. When that gets mixed with the complexity of the various lifecycle-aware components that are part of Android development, there’s plenty of room for pitfalls such as memory leaks: for example, accidentally passing in Activity-scoped dependencies into ViewModels. Hilt being opinionated and designed specifically for Android helps you avoid some of the pitfalls when using vanilla Dagger.” — Marcelo Hernandez, Staff Software Engineer, Tinder
If you’re already using Dagger in your app and want to migrate to Hilt… fear not! Dagger and Hilt can coexist together, apps can be migrated on an as-needed basis.
Less boilerplate
Hilt is opinionated — this means that it makes decisions for you so that you have less code to write. Hilt defines standard components, or dependency graphs, fully integrated with Android framework classes such as Application, activities, fragments, and views. As well as scope annotations to scope instances of types to those components.
“Hilt automatically generates the test application and test component via @HiltAndroidTest. We were able to remove between 20% and 40% of boilerplate wire up test code after migrating to Hilt!” — Jusun Lee, Software Engineer, YouTube
“We are only scratching the surface in terms of migrating to Hilt. However, in one of the modules we migrated to Hilt, we saw +78/-182 in terms of lines changed for this library.” — Marcelo Hernandez, Staff Software Engineer, Tinder
Designed for Android
As opposed to Dagger that is a dependency injection solution for the Java programming language applications, Hilt solely works in Android apps. Some annotations such as
@HiltAndroidApp , @AndroidEntryPoint , or @HiltViewModel are specific to Hilt and define an opinionated way to do DI on Android.
“Hilt finally offers built-in Android lifecycle aware Dagger components. With Hilt, we can solely focus on Dagger @Modules without having to worry about Components, Subcomponents, the Component provider pattern, and so on.” — Marcelo Hernandez, Staff Software Engineer, Tinder
Standardization of components and bindings
For those that know about Dagger, Hilt simplifies the dependency graph by using a monolithic component system to generate less code at compile time.
“With Hilt’s monolithic component system, binding definitions are provided once and shared across all classes that use that component. This is a big win as previously, YouTube used a polylithic component system where modules were manually wired-up into custom components and there were many duplicate binding definitions.” — Jusun Lee, Software Engineer, YouTube
“As our Gradle modules separation allows for feature development in isolation, it became easy to be too creative when using Dagger. We’ve found that migrating those modules over to Hilt has actually exposed flaws in which we were inadvertently violating the separation of concerns.” — Marcelo Hernandez, Staff Software Engineer, Tinder
Integration with other Jetpack libraries
You can use your favourite Jetpack libraries with Hilt out of the box. We provide direct injection support for ViewModel, WorkManager, Navigation, and Compose so far.
Learn more about Jetpack support in the docs.
“I really appreciate how Hilt works out of the box with ViewModels and how it eliminates the boilerplate of having to set up a ViewModel.Factory provider with vanilla Dagger.” — Marcelo Hernandez, Staff Software Engineer, Tinder
Resources to learn Hilt
Hilt is Jetpack’s recommended DI solution for Android apps. To learn more about it and start using it in your apps, check out these resources:
- Learn about the benefits of dependency injection here.
- Documentation to learn how to use Hilt in your app.
- Migration guide from Dagger to Hilt.
- Codelabs to learn Hilt in a step-by-step approach: Using Hilt in your Android app and Migrating from Dagger to Hilt.
- Code samples! Check out Hilt in action in the Google I/O 2020 and Sunflower apps.
- Cheat sheet to quickly see what the different Hilt and Dagger annotations do and how to use them.
Huge thanks to Marcelo Hernandez from the Android Tinder team (Twitter profile), and Jusun Lee from the Android YouTube team for taking the time to talk about how and why they’re adopting Hilt in their apps.
Источник
Hilt еще один DI?
Встречайте Hilt — Dependency Injection (DI) в JetPack, но это не правда, так как Hilt это просто обертка для Dagger2. Для небольших проектов сможет встать более удобным инструментом и хорошо интегрируется с остальными продуктами в JetPack.
Не буду описывать как добавить в проект, все хорошо описано в статье
Зачем?
Dagger2 довольно сложный DI, можно легко запутаться, что и куда поместил. Придумывать бесконечное количество компонентов и увеличивается количество абстракций. И чем больше проект, тем больше увеличивается путаница.
И если хочется использовать Dagger2, но с минимальными усилиями, то для этого как раз и был придуман Hilt.
Что упростили для нас:
- Готовые компоненты (из названий понятно к чему относятся)
- ApplicationComponent
- ActivityRetainedComponent
- ActivityComponent
- FragmentComponent
- ViewComponent
- ViewWithFragmentComponent
- ServiceComponent
- В модуле указываешь в какой компонент добавить
- Через @AndroidEntryPoint Hilt компилятор генерирует весь bolierplate для создания компонента и хранения (например, ActivityRetainedComponent сохранит сущность после поворота экрана, ActivityComponent пересоздаст заново).
Такой код выглядит довольно элегантно (весь boilerplate за нас сгенерируется)
Особенности
Application обязателен
Необходимо объявить Application и пометить @HiltAndroidApp, без него Hilt не заработает.
Иерархическая зависимость
Если хотите использовать Hilt в фрагментах, то Activity которая содержит эти фрагменты обязательно помечать аннотацией @AndroidEntryPoint
Если View пометить @WithFragmentBindings то Fragment должен быть с аннотацией @AndroidEntryPoint, а без этой аннотации зависит от того куда инжектимся Activity или Fragment
Объявление модулей
Все как в Dagger2, но нет необходимости добавлять модуль в компонент, а достаточно использовать аннотацию @InstallIn. Это и понятно, так как компоненты не доступны для редактирования.
При добавления Hilt, все модули должны быть с @InstallIn, либо компилятор ругнется, что аннотация отсутствует.
Кастомные Component и Subcomponent
Создать их конечно можно, так как там Dagger2, но теряется тогда весь смысл Hilt и все придется писать вручную. Вот что предлагается в документации:
Поддержка многомодульности
Все это есть, но только когда используем только готовые компоненты. Но если добавить для каждого модуля свои компоненты (как советуют тут), то лучше использовать Dagger2.
Ограничения для @AndroidEntryPoint
Что внутри
Hilt работает следующим образом:
- Генерируются Dagger Component-ы
- Генерируются базовые классы для Application, Activity, Fragment, View и т.д, которые помечены аннотацией @AndroidEntryPoint
- Dagger компилятор генерирует статический коды
Как устроено хранение ActivityRetainedComponent
Не стали усложнять и просто поместили компонент в ViewModel из arch библиотеки:
Плюсы:
- Более простое использование чем Dagger2
- Добавление модулей через аннотацию выглядит довольно удобно (в несколько уже не поместишь)
- Код чище и много boilerpate спрятано.
- Все плюсы от Dagger2 (генерация статического кода, валидация зависимостей и т.д.)
- Удобен для небольших проектов
Минусы:
- Тяжело избавится, не только захочется убрать или заменить, но и просто перейти на Dagger2
- Тяжело добавить кастомные компоненты, что ограничивает использование с крупных проектах
- Наследует минусы Dagger2 и еще больше увеличивает время сборки
- Иерархическая зависимость, например, нельзя использовать в Fragment без Activity c @AndroidEntryPoint
Источник
Android hilt release notes
Extensions for Dagger Hilt
With this library, it is possible to delegate properties to additional objects.
Generating Hilt Modules
@Bound and @BoundTo
Marks implementation bound to the given supertype in the given component.
@Bound annotation (added in v1.1.0) works exactly like @BoundTo annotation, but it implicitly uses the direct supertype of the annotated class. It may only annotate classes having exactly one direct supertype, excluding java.lang.Object .
will generate module:
Since release 1.1.0, component property is optional, and set to SingletonComponent by default.
Marks factory method for the class returned by the annotated function.
For example, for a Room database:
and a database factory:
and a database factory provider:
annotation processor will generate modules:
Since release 1.1.0, component property is optional, and set to SingletonComponent by default.
@TestBound , @TestBoundTo and @TestFactoryMethod
Version 1.1.0 introduces additional test annotations that can be used to generate modules annotated with @TestInstallIn , instead of @InstallIn :
- @TestBound (instead of @Bound )
- @TestBoundTo (instead of @BoundTo )
- @TestFactoryMethod (instead of @FactoryMethod )
Test module generated using @TestBound and/or @TestBoundTo will replace the module generated using @Bound and/or @BoundTo .
Test module generated using @TestFactoryMethod will replace the module generated with @FactoryMethod .
Hilt Testing Extensions
Must be used as debugImplementation dependency to properly register EmptyFragmentActivity in manifest.
Testing Fragments With Hilt
Works exactly like FragmentScenario, but supports Hilt dependency injection in fragments.
Источник
Hilt+Retrofit+Coroutine
В данной статье хочется показать подробный пример использования Hilt совместно с Retrofit, запросы к бд будут ассинхронно выполнять с помощью Coroutines.
Hilt — это библиотека для внедрения зависимостей для Android, которая сокращает количество шаблонов для ручного внедрения зависимостей в ваш проект. https://developer.android.com/training/dependency-injection/hilt-android
Retrofit — типобезопасный HTTP-клиент для Android и Java. Позволяет нам не особо напрягясь делать GET, POST, PUT и другие запросы.
Coroutines — шаблон проектирования параллелизма, который можно использовать в Android для упрощения кода, который выполняется асинхронно.
Подключение зависимостей
В top-level build file, то есть build.gradle который находиться в самой папке нашего проекта, а не в app, добавляем следующее:
Далее, уже в build.gradle, который находится в папочке app, Navigation подключаем для удобства ориентации https://developer.android.com/guide/navigation/navigation-getting-started:
Теперь расставим аннотации
В соответствии с документацией : «Все приложения, использующие Hilt, должны содержать класс Application, помеченный @HiltAndroidApp «.
Поэтому создаём класс MyApplication, который наследуем от Application:
Также не забываем включить этот класс в AndroidManifest, чтобы при сборке всего приложения использовался именно наш класс:
Теперь добавляем аннотацию @AndroidEntryPoint к MainFragment и MainActivity. Эта аннотацию говорит Hilt, чтобы он генерировал классы Component https://developer.android.com/training/dependency-injection/hilt-android#generated-components . Каждый компонент отвечает за зависимости своего класса, ActivityComponent за Activity, FragmentComponent за Fragment. Именно эта аннотация позволяет нам в дальнейшеи привязывать зависимости к Fragment, Activity и т.д.. Также если вы указали эту аннотацию, например, у какого-то фрагмента, её также необходимо указать у Activity, к которой он привязан. Дополнительно про иерархию можно посмотреть по ссылке.
Далее, в соответствии с архитектурой MVVM нам нужно создать ViewModel, которая будет содержать наши LiveData, данные в которые мы будем загружать из MainRepository:
Аннотация @Singleton говорит Hilt, что наш MainRepository будет привязан к SIngletonComponent, то есть к Application. Первый раз, когда где-то будет создаваться MainRepository, он создаст сам экземпляр класса, в последующие разы будет доставаться тот же самый, созданный в 1 раз MainRepository. Но чтобы Hilt знал, как создавать репозиторий, нужно использовать вот такой синтаксис : @Inject constructor(. ). Если мы далее захотим где-то в коде получить MainRepository, то сделать это можно так: @Inject lateinit var repository: MainRepository.
Теперь объявим ViewModel:
В Hilt для ViewModel есть специальная аннотация @HiltViewModel , ViewModel также как и раньше будут привязаны к жизненным циклам Fragment или Activity, смотря где вы захотите её создать. Также необходимо указать @Inject, по причинам описанным выше, а тк Hilt уже знает, как создавать repository, всё будет отлично работать(при указании @Inject , Hilt должен знать, как создавать объекты, которые находятся в constructor). Теперь во фрагменте мы можем следующим образом получить нашу ViewModel:
Возникает резонный вопрос, зачем все эти заморочки с Hilt, если мы и до этого могли получить ViewModel с помощью by viewModels. Всё дело в том, что так можно сделать, если у нашей ViewModel пустой конструктор, что в случае с MainViewModel не так. Поэтому пришлось бы написать гораздо больше шаблонного кода, использование которого мы и хотим избежать.
Retrofit
При использовании Retrofit также необходимо разрешение на использование интернета, для получения данных удалённой базы данных(в данном примере используется Firebase), поэтому перед тегом
Cоздадим data class, в котором будут храниться данные о пользователе:
Теперь создаём interface, нужный для использования Retrofit, где пропишем все нужные нам запросы:
Пока у нас 1 запрос GET. Аннотация @Path означает, что значение userId будет подставляться в сегмент URL
В дальнейшем тут можно добавить логику обработки ответа: кода ответа, сообщения об ошибке и т.д..
Далее, поскольку у интерфейса нет конструктора, а значит Hilt не может знать как его реализовать. Также Retrofit сторонняя библиотеке, значит о ней Hilt тоже ничего не знает, как же тогда поступить, чтобы Hilt знал как предоставлять экземпляры того или иного класса? На помощь нам придёт Module, он представляет собой набор методов, которые указывают как создать экземпляр нужного нам класса:
Аннотация @InstallIn говорит в контексте чего будут доступны наши зависимости. В данном случае мы хотим получать их в любом месте приложения и поэтому указываем SingletonComponent. Далее мы указываем аннотацию @Provides, чтобы Hilt знал как предоставлять экземпляры типов Retrofit и т.д.. Аннотация @Singleton , как было сказано ранее, говорит Hilt, чтобы он не пересоздавал каждый раз новый экземпляр, а предоставлял уже имеющийся(если ни разу ещё не был создан, то он его создаст).
Заметим, что мы также говорим, как предоставлять экземпляр String, это значит, что если мы где-то создадим класс по типу такого:
То при его получении с помощью:
И вызове метода print(), будет печататься именно BASE_URL.
Передаём данные во ViewModel
Теперь по сути основная работа выполнена, осталось поместить наши данные во ViewModel, а потом из фрагмента достать LiveData и следить за изменением данных в ней.
Прокидываем данные через репозиторий:
Потом превращаем их в LiveData во ViewModel:
Вызов emit () приостанавливает выполнение блока до тех пор, пока значение LiveData не будет установлено в основном потоке. То есть пока наша suspend fun не выполниться.
Теперь наконец отобразим данные, которые мы получаем из Firebase. Для этого в MainFragment добавим 2 Textview:
И в MainFragment будем устанавливать значения, которые мы получили из ViewModel:
Результат(конечно не очень красивый, но мы к этому и не стремились):
Вот и всё, простой пример использования Hilt+retrofit готов! Полный код доступен по ссылке.
Источник