Привет, Хабр. Для будущих студентов курса "Android Developer. Professional" подготовили традиционный перевод материала.
Также приглашаем всех желающих на вебинар по теме «Профилируем и ускоряем Gradle сборки». На занятии участники вместе с экспертом:
— научатся искать узкие места в сборках с помощью gradle-profiler, scan и visualVM;
— научатся правильно конфигурировать Gradle;
— рассмотрят другие возможности для оптимизации и ускорения сборок на большом проекте.
Dagger и Koin, без сомнения, являются двумя самыми популярными фреймворками для внедрения зависимостей на Android. Обе эти библиотеки служат одной цели и кажутся очень похожими, но работают они по-разному.
А при чем здесь Hilt? Hilt — это библиотека, которая использует Dagger под капотом и просто упрощает работу с ним, поэтому все, что я говорю здесь о Dagger, применимо и к Hilt.
В этой статье я не буду подталкивать вас к решению, какую из этих библиотек выбрать. Вместо этого я хочу показать вам, чем они отличаются внутри, и каковы могут быть последствия от этих различий для вашего приложения.
Dagger
Если мы хотим, чтобы Dagger предоставил экземпляр какого-либо класса, все, что нам нужно сделать, это добавить аннотацию @Inject
к конструктору.
Добавление этой аннотации приведет к тому, что Dagger сгенерирует фабрику (Factory) для этого класса во время компоновки. В данном случае, поскольку имя класса — CompositeAdapter
, он сгенерирует класс с именем CompositeAdapter_Factory
.
Этот класс содержит всю информацию, необходимую для создания экземпляра класса CompositeAdapter
.
Как видите, фабрика реализует метод get()
, который возвращает новый экземпляр класса CompositeAdapter
. Фактически это метод, указанный в интерфейсе Provider, который реализует этот класс. Другие классы могут использовать интерфейс Provider для получения экземпляра класса.
Что, если мы будем использовать Hilt вместо Dagger?
В этом примере особой разницы вы бы не заметили. Hilt — это библиотека, которая использует Dagger внутри себя, а класс, который я вам показал, генерируется Dagger. Если вы используете Hilt, он создает для нас пару дополнительных классов, которые упрощают использование Dagger и сокращают количество шаблонного кода, который нам нужно написать. Но основная часть остается прежней.
Koin
У Koin совершенно другой подход к управлению зависимостями, чем у Dagger и, конечно, чем у Hilt. Чтобы зарегистрировать зависимость в Koin, мы не используем никаких аннотаций, поскольку Koin не генерирует никакого кода. Вместо этого мы должны предоставить модули с фабриками, которые будут использоваться для создания экземпляров каждого класса, который понадобится в нашем проекте.
Ссылка на эти фабрики добавляется Koin в класс InstancesRegistry
, который содержит ссылки на все фабрики, которые мы написали.
Ключ в этой map — это полное имя класса или имя, которое мы предоставили, используя именованный параметр. Значение — это написанная нами фабрика, которая будет использоваться для создания экземпляра класса.
Чтобы получить зависимость, все, что нам нужно сделать, это вызвать get()
(например, в фабрике) или вызвать делегированное свойство inject()
в активити или фрагментах, которые под капотом лениво вызывают get()
. Метод get()
найдет фабрику, зарегистрированную для класса данного типа, и внедрит ее туда.
Какие последствия для приложения?
Есть некоторые последствия того факта, что Dagger генерирует код для предоставления зависимостей, а Koin — нет.
1. Обработка ошибок
Поскольку Dagger — это среда внедрения зависимостей во время компиляции, если мы забыли предоставить какую-либо зависимость, мы узнаем о нашей ошибке практически мгновенно, потому что наш проект не будет собран.
Например, если мы забыли добавить аннотацию @Inject
к конструктору CompositeAdapter
и попытаемся внедрить его во фрагмент, сборка завершится неудачно с соответствующей ошибкой, которая показывает нам, что именно пошло не так.
С Koin ситуация иная. Поскольку он не генерирует никакого кода, если мы забыли добавить фабрику для класса CompositeAdapter
, приложение будет собрано, но закрашится с RuntimeException
, как только мы запросим экземпляр этого класса. Это может произойти при запуске приложения, поэтому мы можем заметить это сразу, но это также может произойти позже, на каком-то второстепенном экране или когда пользователь выполняет какое-то конкретное действие.
2. Влияние на время сборки
В том, что Koin не генерирует никакого кода, есть некоторое преимущество: это оказывает гораздо меньшее влияние на время сборки. Dagger необходимо использовать процессор аннотаций для сканирования нашего кода и создания соответствующих классов. Это может занять некоторое время и замедлить нашу сборку.
3. Влияние на производительность во время выполнения
С другой стороны, поскольку Koin разрешает зависимости во время выполнения, это результирует в немного более худшей производительности во время выполнения.
На сколько? Чтобы оценить разницу в производительности, мы можем взглянуть на этот репозиторий, где Рафа Васкес измерил и сравнил производительность этих двух библиотек на разных устройствах. Тестовые данные были подготовлены таким образом, чтобы имитировать несколько уровней транзитивных зависимостей, т.е. это не просто приложение-болванка с 4 классами.
Как видите, Dagger практически не влияет на производительность при запуске. С другой стороны, в Koin мы видим, что настройка занимает значительное время. Внедрение зависимостей в Dagger также немного быстрее, чем в Koin.
Резюме
Как я сказала в начале этой статьи, моя цель не в том, чтобы указать вам, какую из этих библиотек использовать. Я использовала Koin и Dagger в двух разных, довольно больших проектах. Честно говоря, я думаю, что решение, какой из них выбрать, Dagger или Koin, гораздо менее важно, чем буквально что-угодно, что позволяет вам писать чистый, простой и легкий для модульного тестирования код. И я думаю, что все эти библиотеки: Koin, Dagger и Hilt действительно служат этой цели.
У всех этих библиотек есть свои сильные стороны, и я надеюсь, что знание того, как они работают, поможет вам принять решение, какая из них лучше всего подойдет именно для вашего приложения.
- Узнать подробнее о курсе "Android Developer. Professional".
- Узнать подробнее о курсе "Android Developer. Basic".Смотреть вебинар по теме «Профилируем и ускоряем Gradle сборки».