Как подружить Dagger2 и ViewModel
Бонусом пойдет описание автоматического внедрения в activity и фрагменты и конечно же незабываемый десерт.
Как подружить Dagger2 и ViewModel

картинка полностью отражает суть, все через жопу =)
Небольшой дисклеймер, описывать что такое Dagger и ViewModel я тут не буду, желающие могут начать с этого и этого, но зато бонусом пойдет описание автоматического внедрения в activity и фрагменты и конечно же незабываемый десерт.
Итак описание проблемы, у нас есть некая ViewModel с инжект конструктором, что-то вроде:
class DemoViewModel @Inject constructor(preferences: SharedPreferences, ...) : ViewModel() {
...
}
и мы хотим её внедрить в какую-нибудь activity/фрагмент или еще куда
Проблемы две, во первых при обычном внедрении модель будет пересоздаваться при каждом повороте экрана, во вторых по умолчанию ViewModelProviders не поддерживает никаких конструкторов с параметрами.
Значит нужно создать собственного провайдера моделей, и его внедрять в activity, а он уже в качестве конструктора будет получать список (точнее map) моделей. Откуда возьмется список? Слава богу одна из фишек dagger2 поддержка мульбайндинга, и это точно нам нужно.
Реализация
Для начала создадим то, что у нас будет ключом, логично для этого использовать класс модели.
[embed]
Это то чем потом мы будем анонсировать модели в модуле:
[embed]
Ключ есть, создаем свою фабрику
[embed]
Обратите внимание что сам класс не помечен как @Singleton тут есть несколько подходов, если вы хотите держать все модели в приложении на протяжении работы в одной фабрике то помечайте его как синглтон, сделайте один модуль со всеми моделями и соответственно добавьте туда же биндинги моделей
@Module
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(
factory: ViewModelFactory): ViewModelProvider.Factory
...
//модели
Если же у вас приложение тяжелое с несколькими activity в каждой их которых с пяток фрагментов и тяжелыми моделями (тяжелыми в смысле объема данных/картинок и прочего) то лучше привязывать фабрику к ActivityModule т.е. сделать модуль ViewModelModule и уже этот модуль вписывать как зависимость к модулям активности, и в них же хранить биндинги моделей.
Как это выглядит в activity/фрагменте
[embed]
Главное! Фабрика инжектируется, модель нет!
На самом деле в котлине это можно сделать еще лучше
[embed]
и в место
demoViewModel = ViewModelProviders.of(this, viewModelFactory)[DemoViewModel::class.java]
получаем
demoViewModel = injectViewModel(viewModelFactory)
Что именно за модель компилятор возьмёт из типа переменной, магия inline.
О автоматическом инжекте
Что если не хочется каждый раз в onCreate activity и фрагмента писать одно и тоже: getApplication().inject(this) бла бла бла
Тут нам поможет Application.ActivityLifecycleCallbacks, но обо всем по порядку.
Создаем компонент приложения
[embed]
Создаем AppApplication (не забываем в манифесте прописать свой класс)
[embed]
Вся магия именно в этом autoInject()
[embed]
Итак мы регистрируем ActivityLifecycleCallbacks и проходим собственно по activity, и фрагментам. Для фрагментов у нас есть специальный придуманный интерфейс FragmentInjectable по сути метка что этот фрагмент участвует в инжектах, ну и наше activity мы наследуем от DiActivity.
В чем минус, в модулях activityModule одну и туже activity нужно представлять по разному т.е. вот у нас есть биндинг того что
- MyActivity: DiActivity() это Activity а вот у нас бинд того что
- MyActivity это FragmentActivity
это все одна MyActvity, на даггеру важно найти конкретный тип класса, а не то что это все предки одной той же MyActivity.
Да в биндигах модулей не забываем свои собственные
@ActivityScope, а в фрагментах@FragmentScope
Десерт
После того как все сделано модно, стильно, молодежно и у нас всякие инжекты в activity, фрагменты, сервисы и прочие бродкасты, а наше приложение опубликовано в google play, вы начнете получать странные ошибки и RuntimeException особенно на устройствах Samsung.
Суть будет сводиться к тому, что почему-то внутри всех этих сервисов и бродкастов иногда… не каждый раз, и не у всех, примерно у 0,1%-1%, вместо нашего наследника Application в котором собственно все и настраивается, система запускает просто дефолтный Application.
А вот так вот вуаля, вспоминаем картинку начала статьи :-D
Но это уже совсем другая тема, смотреть её можно здесь
메타데이터
- post_id
- 81d4cd59f642
- slug
- dagger2-viewmodel-81d4cd59f642
- url
- https://medium.com/@vit.onix/dagger2-viewmodel-81d4cd59f642
- canonical_url
- https://medium.com/@vit.onix/dagger2-viewmodel-81d4cd59f642
- author_url
- https://medium.com/@vit.onix
- status
- ok
- fetched_at
- 2026-07-30 02:57:19