← Back to list

Как подружить Dagger2 и ViewModel

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

Виталий Шеянов · 2018-06-17 20:29 · 353 claps · 2.7 min read
#dagger-2 #viewmodel #android-app-development #kotlin
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

Как подружить 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

Но это уже совсем другая тема, смотреть её можно здесь

[embed]RuntimeException with Dagger 2 on Android 7.0 and Samsung devices On my Google Play console I see quite a lot crash reports since I started to use Dagger 2, but only on Android 7.0 and…stackoverflow.com

[embed]Dagger Application injection NPE · Issue #748 · google/dagger I'm trying to inject some objects on application, in order to access them whenever I want. So, on my application I'm…github.com


메타데이터
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