Dagger2 инъекция ViewModel без конструктора @Inject

У меня есть большие классы Android ViewModel, которые, как правило, имеют много зависимостей (большинство из них - это DAO из Room, по одному на таблицу SQLite). У некоторых больше 10 зависимостей.

Это нормально, но конструктор @Inject переполнен аргументами и содержит только шаблонный код для установки введенных членов из аргументов конструктора.

Я хотел переключиться на «обычные» внедренные члены, индивидуально идентифицируемые аннотацией @Inject, как и другие (тупые) классы.

Это не работает для классов, связанных с Android (хотя ViewModels рекламируются как не зависящие от Android, например, они не используют платформу Android), таких как действия и фрагменты.

Чтобы решить эту проблему, используйте фабрику, которая вводится из класса Application с использованием хороших интерфейсов HasActivityInjector, HasServiceInjector и т. Д.

Dagger не предоставляет никаких HasViewModelInjector, поэтому, если я буду продолжать вводить элементы по отдельности вместо того, чтобы вводить конструктор, вот что мне дано:

ошибка: [dagger.android.AndroidInjector.inject (T)] XXXViewModel не может быть предоставлен без конструктора @Inject или из метода с аннотацией @ Provides. Этот тип поддерживает внедрение членов, но не может быть предоставлен неявно.

Если я создаю модуль с аннотацией @Provides для создания ViewModel, он не вводит отдельные элементы.

Я что-то пропустил (мое последнее предложение - самое важное в моем вопросе) или просто невозможно ввести члены, и я должен внедрить конструктор?


Немного кода.

Что я хочу:

class MyViewModel extends ViewModel {
    @Inject
    MyDao myDao;
}

по сравнению с тем, что мне нужно сделать:

class MyViewModel extends ViewModel {
    private final MyDao myDao;

    @Inject
    MyViewModel(MyDao myDao) {
        this.myDao = myDao;
    }
}

Первый блок кода (то, что я хочу) требует этого метода в модуле:

@Provides
MyViewModel provideMyViewModel() {
    return new MyViewModel();
}

но в этом случае поле myDao пусто. Как вставить аннотированные @Inject члены?

Я хочу избежать использования второго блока кода, который имеет тенденцию создавать огромный конструктор, раздуваемый множеством аргументов, если мне нужно ввести много членов.


person Benoit Duffez    schedule 23.04.2018    source источник
comment
Я прочитал этот хороший ответ, в котором объясняется, что мы должны всегда предпочитать внедрение конструктора или предоставлять его из модуля, в котором вы может выполнять дополнительные действия по настройке; однако это не распространяется на инъекцию члена.   -  person Benoit Duffez    schedule 24.04.2018
comment
Вы можете использовать мой метод, stackoverflow.com/a/53956997/7558125   -  person Pratik Mhatre    schedule 28.12.2018
comment
@PratikMhatre: спасибо, но я перешел на Koin, у которого гораздо более простой API.   -  person Benoit Duffez    schedule 28.12.2018


Ответы (1)


Есть несколько способов инъекции, и я думаю, вы имеете в виду инъекцию в поле. Внедрение поля, в отличие от внедрения конструктора, должно запускаться вручную. Для этого определите метод в вашем компоненте с моделью представления в качестве параметра.

void inject(ViewModel viewModel)

А затем, возможно, вызовите этот метод из конструктора модели представления.

class MyViewModel extends ViewModel {
    private final MyDao myDao;

    @Inject
    MyDao myDao;

    public MyViewModel() {
        MyComponent mycomponent = DaggerMyComponent.....
        myComponent.inject(this);
    }
}
person tompee    schedule 24.04.2018
comment
Хорошо, я знал, что нужно вводить вручную, просто не мог понять. - person Benoit Duffez; 24.04.2018
comment
На самом деле я отредактировал InjectableViewModelFactory (который можно найти повсюду в сети) для внедрения с экземпляром приложения (чтобы я мог получить доступ к компоненту приложения), а затем вызвал application.getComponent().inject((MyViewModel) creator.get()). Ваш ответ мне очень помог, спасибо! - person Benoit Duffez; 24.04.2018