Как используется Dependency Injection в Android
Что проверяют
Понимаете ли вы, зачем DI нужен на практике: подмена зависимостей в тестах, управление временем жизни объектов, сборка графа без ручной передачи. Следом спрашивают про скоупы в Hilt и чем Hilt отличается от Koin.
Как отвечать
Суть DI: класс получает зависимости снаружи, чаще через конструктор, и не создаёт их сам. В Android мешают системные классы: Activity и Fragment создаёт система, конструктор туда не передашь. Отсюда библиотеки:
- Hilt: надстройка над Dagger, граф проверяется при компиляции, ошибки видны на сборке. Код генерируется через KSP, kapt остался для старых проектов;
- Koin: DSL и разрешение зависимостей в рантайме. Старт проще, но забытая зависимость падает при открытии экрана, а не на сборке;
- ручной DI: контейнер в
Application, для небольшого приложения этого хватает.
Как это выглядит в Hilt:
@HiltAndroidAppнаApplication,@AndroidEntryPointнаActivityиFragment;@Inject constructorдля своих классов, интерфейсы привязываются через@Binds, чужие классы вродеRetrofitчерез@Provides;@InstallIn(SingletonComponent::class)задаёт компонент модуля, есть такжеViewModelComponentиActivityComponent;- ViewModel помечается
@HiltViewModelи берётся черезby viewModels()илиhiltViewModel()в Compose,SavedStateHandleприходит сам, параметры времени выполнения передают через@AssistedInject; - в тестах
@HiltAndroidTestи@TestInstallInподменяют сетевой модуль фейковым.
Где спотыкаются
Путают скоуп и время жизни. Без аннотации скоупа Hilt создаёт новый экземпляр на каждую инъекцию, а @Singleton на всём подряд держит объекты в памяти до конца работы приложения.
Передают Activity в синглтон, например в репозиторий. После поворота экрана старая Activity не освобождается, это утечка. Долгоживущим объектам нужен @ApplicationContext.
Пишут ViewModel с @Inject constructor без @HiltViewModel и внедряют её полем. Такой объект не переживает поворот экрана и не получает onCleared().