Привет, Android разработчики!
Хотел бы рассказать вам про мою библиотеку (Anvil Utils) - кодогенератор позволяющий слегка уменьшить количество болилерплейта в многомодульных проектах использующих Dagger 2 и Anvil.
В чём собственно проблема?
Представьте: у вас есть многомодульное приложение.
Почти наверняка у вас есть набор фичей, каждая из которых состоит из api
и implementation
модулей.
Представим, что один из модулей предоставляет фабрику какой-то сущности (например, Decompose компонента).
Если у вас в проекте используется Dagger 2, то реализацию этой фабрики логичнее всего отдать ему, используя Assisted Injection.
Однако, вам всё равно необходимо каким-то образом предоставить биндинг этой Assisted Factory к вашему публичному интерфейсу из модуля api
.
Попробуем посмотреть на варианты как это можно сделать.
Голый Dagger 2
Предположим, у вас есть подобный публичный API вашего feature модуля.
interface MyClass {
interface Factory {
fun create(param1: String, param2: Int): MyClass
}
}
Обычно чтобы реализовать подобный API и сделать его доступным в вашем графе зависимостей, нужно:
Создаём
@AssistedFactory
вimplementation
модуле, наследуем её от публичного интерфейса фабрикиMyClass.Factory
Пишем Dagger модуль который занимается биндингом сгенерированной Dagger 2 фабрики к публичному интерфейсу фабрики из модуля
api
.
Вот пример:
class DefaultMyClass @AssistedInject constructor(
@Assisted param1: String,
@Assisted param2: Int
) : MyClass {
@AssistedFactory
interface Factory: MyClass.Factory {
override fun create(param1: String, param2: Int): DefaultMyClass
}
}
@Module
interface MyClassFactoryBindingModule {
@Binds
fun bindFactory(impl: DefaultMyClass.Factory): MyClass.Factory
}
Также, вам придётся поддерживать руками набор параметров во всех трёх сущностях:
Параметры конструктора
Аргументы публичного интерфейса фабрики
Аргументы assisted фабрики Dagger
Достаточно много боилерплейта для такой простой задачи, не так ли?
Давайте упростим!
Используем Anvil
Anvil это плагин компилятора Kotlin который помогает значительно уменьшить количество боилерплейта необходимого для использования Dagger 2 в вашем приложении.
Также, будучи правильно сконфигурированным, позволяет увеличить скорость сборки проекта путём избавления от необходимости запуска процессора аннотаций Dagger 2 в ваших фича-модулях.
Я не буду погружаться в подробности того что такое Anvil и почему вам стоит подумать о том чтоб использовать его в вашем проекте. Возможно, напишу о нём статью попозже.
Пока что, если интересует, можете прочитать документацию: https://github.com/square/anvil
Благодаря Anvil, мы можем избавиться от нашего MyClassFactoryBindingModule
и использовать вместо него аннотацию @ContributesBinding
:
class DefaultMyClass @AssistedInject constructor(
@Assisted param1: String,
@Assisted param2: Int
) : MyClass {
@ContributesBinding(AppGraph::class, MyClass.Factory::class)
@AssistedFactory
interface Factory: MyClass.Factory {
override fun create(param1: String, param2: Int): DefaultMyClass
}
}
Anvil сгенерирует для нас соответствующий модуль который создаст биндинг DefaultMyClass.Factory
к MyClass.Factory
.
Этот подход уменьшает количество боилерплейта, но он всё ещё тут!
Нам необходимо вручную следить за соответствием публичного интерфейса фабрики, интерфейса Assisted фабрики, и параметров конструктора.
Может есть вариант как упростить это ещё сильнее?
Встречайте @ContributesAssistedFactory !
Благодаря моей библиотеке Anvil Utils, вы можете попрощаться с необходимостью вручную создавать и поддерживать Assisted фабрики. Аннотация @ContributesAssistedFactory
всё сделает за вас!
Она автоматически сгенерирует assisted фабрику и забиндит её в соответствующий скоуп при помощи @ContributesBinding
.
Теперь мы можем полностью удалить интерфейс assisted фабрики и аннотировать наш DefaultMyClass
аннотацией @ContributesAssistedFactory
:
@ContributesAssistedFactory(AppGraph::class, MyClass.Factory::class)
class DefaultMyClass @AssistedInject constructor(
@Assisted param1: String,
@Assisted param2: Int
) : MyClass
И всё! Anvil Utils сгенерирует всё что нужно сама.
Пример из реального проекта
Для того чтобы продемонстрировать что польза не иллюзорна, я создал пулл реквест в прекрасный open source проект - приложение-компаньон для Flipper - тамагочи для хакеров.
Можете ознакомиться с ним по этой ссылке, если интересно: https://github.com/flipperdevices/Flipper-Android-App/pull/798
В заключение
Если вы используете в своём проекте Anvil и у вас есть идеи что ещё можно упростить при помощи кодогенерации - открывайте пулл реквесты, возможно я реализую ?