dagger2 and android: модуль загрузки, который внедряет viewmodel на карту - PullRequest
0 голосов
/ 15 сентября 2018

Я начал использовать Dagger2, так что еще есть чему поучиться.Мне интересно, может ли кто-нибудь указать мне правильное направление.

Итак, я создал модуль для регистрации моделей представлений, используемых в моей деятельности.Это выглядит так:

@Module
abstract class ViewModelModule {
    @Binds
    internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainActivityViewModel::class)
    internal abstract fun bindMainActivityViewModel(viewModel: MainActivityViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(ShowDetailsViewModel::class)
    abstract fun bindShowDetaislViewModel(viewModel: ShowDetailsViewModel): ViewModel
}

ViewModelKey - это простой класс вспомогательных аннотаций, который выглядит следующим образом:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey (val value: KClass<out ViewModel>) {
}

ViewModelModule загружается моим основным компонентом приложения (используетсядля создания приложения):

@Singleton
@Component(
        modules=[
            AndroidSupportInjectionModule::class,
            AppModule::class,
            DatabaseModule::class,
            NewsServiceModule::class,
            JobBindingModule::class,
            ViewModelModule::class,
            PreferencesModule::class,
            ActivityBindingModule::class
        ]
)
interface AppComponent: AndroidInjector<MyApp> {
    @Component.Builder
    abstract class Builder: AndroidInjector.Builder<MyApp>()
}

А вот код для ActivityBindingModule, отвечающего за настройку подкомпонентов (в данном случае, операций, используемых моим приложением):

@Module
abstract class ActivityBindingModule {
    @ActivityScoped
    @ContributesAndroidInjector()
    internal abstract fun mainActivity(): MainActivity

    @ActivityScoped
    @ContributesAndroidInjector
    internal abstract fun showDetailsActivity(): ShowDetailsActivity
}

Внутри каждого действия создается модель представления с помощью кода, который выглядит следующим образом (вызывается из метода onCreate):

//view model code
_viewModel = ViewModelProviders.of(this, viewModelFactory)[ShowDetailsViewModel::class.java]

И, как и следовало ожидать, вводится viewModelFactoryas field:

@Inject lateinit var viewModelFactory: ViewModelProvider.Factory

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

И, для полноты, вот кодна мой взгляд модель фабрики:

@Singleton
class ViewModelFactory @Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T
        = viewModels[modelClass]?.get() as T

Этот код работает, но, похоже, его можно улучшить.После прочтения документации у меня сложилось впечатление, что я мог бы провести рефакторинг своего ViewModeModule, чтобы он просто создавал мой ViewModelFactory и перемещал каждое из объявлений модели представления в отдельный модуль (так, чтобы каждое из них могло быть введено тольков «правильной» деятельности).

Чтобы проверить это, я начал с перемещения ShowDetailsViewModel в новый модуль, который имеет только одну запись:

@Module
internal abstract class DetailsModule {
    @Binds
    @IntoMap
    @ViewModelKey(ShowDetailsViewModel::class)
    abstract fun bindShowDetaislViewModel(viewModel: ShowDetailsViewModel): ViewModel

}

После этого ViewModelModule выглядит следующим образом:

@Module
abstract class ViewModelModule {
    @Binds
    internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainActivityViewModel::class)
    internal abstract fun bindMainActivityViewModel(viewModel: MainActivityViewModel): ViewModel
}

И я обновил ActivityBindingModule, чтобы он выглядел следующим образом:

@Module
abstract class ActivityBindingModule {
    @ActivityScoped
    @ContributesAndroidInjector()
    internal abstract fun mainActivity(): MainActivity

    @ActivityScoped
    @ContributesAndroidInjector(modules = [DetailsModule::class])
    internal abstract fun showDetailsActivity(): ShowDetailsActivity
}

Обратите внимание, что теперь я передаю DetailsModule (который создает экземпляр ShowDetailsViewModel) к аннотации ContributeAndroidInjector, которая применяется к методу showDetailsActivity, потому что эта модель представления используется только этим действием.

Теперь я определенно что-то упускаю, потому что после этого я 'm всегда получает следующее исключение:

java.lang.IllegalStateException: ViewModelProviders.of(th…ilsViewModel::class.java] must not be null

Если я отлаживаю приложение, я вижу, что перемещение ShowDetailsViewModel в его собственную модель не регистрирует его на карте, используемой фабрикой (т. е. карта имееттолько одна запись, которая соответствует MainActivityViewModel, зарегистрированному в ViewModelModule.

Я подумал, что перемещение каждой модели представления объявления в каждый модуль, используемый субкомпонентом, все же должно позволить ему быть зарегистрированным вмамаp вводится модулем, который зарегистрирован в верхнем компоненте.Я ошибся?Что мне не хватает, чтобы сделать эту работу?

Спасибо.

1 Ответ

0 голосов
/ 17 сентября 2018

Проблема заключается в том, что ViewModelFactory означает @Singleton и что он не получит никаких привязок, добавленных вами в ваши подкомпоненты.Удалите область из вашей фабрики или сделайте ее @ActivityScoped (такая же, как у модели представления для действия)

Деятельность (@ActivityScoped) имеет доступ к фабрике (@Singleton), но к фабрике(@Singleton) не имеет доступа к использованию или созданию ViewModel из нижней области (@ActivityScoped).Таким образом, перемещение фабрики в ту же область (@ActivityScoped) даст ей доступ к созданию рассматриваемой модели представления.

...